//! Wave Accounting GraphQL types
//!
//! Types for interacting with Wave's products, customers, and invoices.

use async_graphql::{InputObject, SimpleObject};
use rust_decimal::Decimal;

use crate::services::wave;

// ============================================================================
// Wave Product Types
// ============================================================================

/// Wave product from the accounting system
#[derive(SimpleObject, Clone)]
pub struct WaveProductType {
    /// Wave product ID
    pub id: String,
    /// Product name
    pub name: String,
    /// Product description
    pub description: Option<String>,
    /// Default unit price
    pub unit_price: f64,
    /// Whether this product is sold
    pub is_sold: bool,
    /// Whether this product is archived
    pub is_archived: bool,
    /// Default sales taxes applied to this product
    pub taxes: Vec<WaveTaxType>,
    /// Income account this product is linked to
    pub income_account: Option<WaveAccountType>,
}

impl From<wave::WaveProduct> for WaveProductType {
    fn from(p: wave::WaveProduct) -> Self {
        Self {
            id: p.id,
            name: p.name,
            description: p.description,
            unit_price: p.unit_price,
            is_sold: p.is_sold,
            is_archived: p.is_archived,
            taxes: p.default_sales_taxes.into_iter().map(WaveTaxType::from).collect(),
            income_account: p.income_account.map(WaveAccountType::from),
        }
    }
}

/// Wave tax rate
#[derive(SimpleObject, Clone)]
pub struct WaveTaxType {
    pub id: String,
    pub name: String,
    pub rate: f64,
}

impl From<wave::WaveTax> for WaveTaxType {
    fn from(t: wave::WaveTax) -> Self {
        Self {
            id: t.id,
            name: t.name,
            rate: t.rate,
        }
    }
}

/// Wave account (for income account references)
#[derive(SimpleObject, Clone)]
pub struct WaveAccountType {
    pub id: String,
    pub name: String,
}

impl From<wave::WaveAccount> for WaveAccountType {
    fn from(a: wave::WaveAccount) -> Self {
        Self {
            id: a.id,
            name: a.name,
        }
    }
}

// ============================================================================
// Wave Customer Types
// ============================================================================

/// Wave customer from the accounting system
#[derive(SimpleObject, Clone)]
pub struct WaveCustomerType {
    /// Wave customer ID
    pub id: String,
    /// Customer name
    pub name: String,
    /// Customer email
    pub email: Option<String>,
    /// Customer address
    pub address: Option<WaveAddressType>,
    /// Currency code
    pub currency_code: Option<String>,
}

impl From<wave::WaveCustomer> for WaveCustomerType {
    fn from(c: wave::WaveCustomer) -> Self {
        Self {
            id: c.id,
            name: c.name,
            email: c.email,
            address: c.address.map(WaveAddressType::from),
            currency_code: c.currency.map(|cur| cur.code),
        }
    }
}

/// Wave customer address
#[derive(SimpleObject, Clone)]
pub struct WaveAddressType {
    pub address_line1: Option<String>,
    pub address_line2: Option<String>,
    pub city: Option<String>,
    pub province_code: Option<String>,
    pub province_name: Option<String>,
    pub postal_code: Option<String>,
}

impl From<wave::WaveAddress> for WaveAddressType {
    fn from(a: wave::WaveAddress) -> Self {
        Self {
            address_line1: a.address_line1,
            address_line2: a.address_line2,
            city: a.city,
            province_code: a.province.as_ref().map(|p| p.code.clone()),
            province_name: a.province.map(|p| p.name),
            postal_code: a.postal_code,
        }
    }
}

// ============================================================================
// Wave Invoice Types
// ============================================================================

/// Wave invoice from the accounting system
#[derive(SimpleObject, Clone)]
pub struct WaveInvoiceType {
    /// Wave invoice ID
    pub id: String,
    /// Invoice number
    pub invoice_number: String,
    /// Invoice date (YYYY-MM-DD)
    pub invoice_date: String,
    /// Due date (YYYY-MM-DD)
    pub due_date: Option<String>,
    /// Status (DRAFT, SENT, VIEWED, PAID, OVERDUE)
    pub status: String,
    /// Customer info
    pub customer: WaveInvoiceCustomerType,
    /// Invoice line items
    pub items: Vec<WaveInvoiceItemType>,
    /// Subtotal before taxes
    pub subtotal: WaveMoneyType,
    /// Total including taxes
    pub total: WaveMoneyType,
    /// Amount still due
    pub amount_due: WaveMoneyType,
    /// Amount already paid
    pub amount_paid: WaveMoneyType,
    /// URL to download the PDF
    pub pdf_url: Option<String>,
}

impl From<wave::WaveInvoice> for WaveInvoiceType {
    fn from(i: wave::WaveInvoice) -> Self {
        Self {
            id: i.id,
            invoice_number: i.invoice_number,
            invoice_date: i.invoice_date,
            due_date: i.due_date,
            status: i.status,
            customer: WaveInvoiceCustomerType::from(i.customer),
            items: i.items.into_iter().map(WaveInvoiceItemType::from).collect(),
            subtotal: WaveMoneyType::from(i.subtotal),
            total: WaveMoneyType::from(i.total),
            amount_due: WaveMoneyType::from(i.amount_due),
            amount_paid: WaveMoneyType::from(i.amount_paid),
            pdf_url: i.pdf_url,
        }
    }
}

/// Wave invoice customer reference
#[derive(SimpleObject, Clone)]
pub struct WaveInvoiceCustomerType {
    pub id: String,
    pub name: String,
}

impl From<wave::WaveInvoiceCustomer> for WaveInvoiceCustomerType {
    fn from(c: wave::WaveInvoiceCustomer) -> Self {
        Self {
            id: c.id,
            name: c.name,
        }
    }
}

/// Wave invoice line item
#[derive(SimpleObject, Clone)]
pub struct WaveInvoiceItemType {
    /// Item description
    pub description: String,
    /// Quantity
    pub quantity: f64,
    /// Unit price
    pub unit_price: f64,
    /// Total for this line item
    pub total: WaveMoneyType,
}

impl From<wave::WaveInvoiceItem> for WaveInvoiceItemType {
    fn from(item: wave::WaveInvoiceItem) -> Self {
        Self {
            description: item.description,
            quantity: item.quantity,
            unit_price: item.unit_price,
            total: WaveMoneyType::from(item.total),
        }
    }
}

/// Wave money type with value and currency
#[derive(SimpleObject, Clone)]
pub struct WaveMoneyType {
    /// Amount as string (for precision)
    pub value: String,
    /// Currency code (e.g., "USD", "CAD")
    pub currency_code: String,
    /// Currency symbol (e.g., "$")
    pub currency_symbol: String,
}

impl From<wave::WaveMoney> for WaveMoneyType {
    fn from(m: wave::WaveMoney) -> Self {
        Self {
            value: m.value,
            currency_code: m.currency.code,
            currency_symbol: m.currency.symbol,
        }
    }
}

// ============================================================================
// Input Types for Wave Mutations
// ============================================================================

/// Input for creating a Wave invoice from a Nexus invoice
#[derive(InputObject)]
pub struct CreateWaveInvoiceInput {
    /// Nexus invoice ID to sync to Wave
    pub invoice_id: uuid::Uuid,
    /// Optional memo/notes for the invoice
    pub memo: Option<String>,
    /// Invoice date (YYYY-MM-DD). Defaults to Nexus invoice start_date.
    pub invoice_date: Option<String>,
    /// Due date (YYYY-MM-DD). If not set, Wave uses customer's default terms.
    pub due_date: Option<String>,
}

/// Input for creating a Wave customer from a Nexus customer
#[derive(InputObject)]
pub struct CreateWaveCustomerInput {
    /// Nexus customer ID to sync to Wave
    pub customer_id: uuid::Uuid,
    /// Override email (uses Nexus billing_email if not set)
    pub email: Option<String>,
    /// Currency code (defaults to "USD")
    pub currency: Option<String>,
}

/// Result of Wave invoice creation
#[derive(SimpleObject)]
pub struct CreateWaveInvoiceResult {
    /// Whether the operation succeeded
    pub success: bool,
    /// Error message if failed
    pub error: Option<String>,
    /// The created Wave invoice
    pub invoice: Option<WaveInvoiceType>,
    /// Updated Nexus invoice with wave_invoice_id
    pub nexus_invoice: Option<super::InvoiceType>,
}

/// Result of Wave customer creation
#[derive(SimpleObject)]
pub struct CreateWaveCustomerResult {
    /// Whether the operation succeeded
    pub success: bool,
    /// Error message if failed
    pub error: Option<String>,
    /// The created Wave customer
    pub customer: Option<WaveCustomerType>,
    /// Updated Nexus customer with wave_customer_id
    pub nexus_customer: Option<super::CustomerType>,
}

/// Readiness check for creating a Wave invoice
#[derive(SimpleObject)]
pub struct WaveInvoiceReadiness {
    /// Whether the invoice can be synced to Wave
    pub ready: bool,
    /// List of issues preventing sync (empty if ready)
    pub issues: Vec<String>,
    /// The customer's Wave customer ID (if linked)
    pub wave_customer_id: Option<String>,
    /// Items that can be synced (have wave_service_id)
    pub ready_item_count: i32,
    /// Items missing wave_service_id
    pub missing_wave_link_count: i32,
    /// Total expected amount
    pub total_amount: Decimal,
}

// ============================================================================
// Pagination Types
// ============================================================================

/// Pagination info for Wave queries
#[derive(SimpleObject, Clone)]
pub struct WavePageInfoType {
    pub current_page: i32,
    pub total_pages: i32,
    pub total_count: i32,
}

/// Paginated list of Wave invoices
#[derive(SimpleObject, Clone)]
pub struct WaveInvoiceConnectionType {
    pub page_info: WavePageInfoType,
    pub invoices: Vec<WaveInvoiceListItemType>,
}

/// Lighter Wave invoice type for list views (no items array)
#[derive(SimpleObject, Clone)]
pub struct WaveInvoiceListItemType {
    pub id: String,
    pub invoice_number: String,
    pub invoice_date: String,
    pub due_date: Option<String>,
    pub status: String,
    pub customer: WaveInvoiceCustomerType,
    pub subtotal: WaveMoneyType,
    pub total: WaveMoneyType,
    pub amount_due: WaveMoneyType,
    pub amount_paid: WaveMoneyType,
}

impl From<wave::WaveInvoiceListItem> for WaveInvoiceListItemType {
    fn from(i: wave::WaveInvoiceListItem) -> Self {
        Self {
            id: i.id,
            invoice_number: i.invoice_number,
            invoice_date: i.invoice_date,
            due_date: i.due_date,
            status: i.status,
            customer: WaveInvoiceCustomerType {
                id: i.customer.id,
                name: i.customer.name,
            },
            subtotal: WaveMoneyType::from(i.subtotal),
            total: WaveMoneyType::from(i.total),
            amount_due: WaveMoneyType::from(i.amount_due),
            amount_paid: WaveMoneyType::from(i.amount_paid),
        }
    }
}

// ============================================================================
// Generic Mutation Result
// ============================================================================

/// Generic result for Wave mutations
#[derive(SimpleObject)]
#[graphql(concrete(name = "WaveInvoiceMutationResult", params(WaveInvoiceType)))]
#[graphql(concrete(name = "WaveProductMutationResult", params(WaveProductType)))]
#[graphql(concrete(name = "WaveCustomerMutationResult", params(WaveCustomerType)))]
#[graphql(concrete(name = "WaveBoolMutationResult", params(bool)))]
pub struct WaveMutationResult<T: async_graphql::OutputType> {
    pub success: bool,
    pub error: Option<String>,
    pub data: Option<T>,
}

// ============================================================================
// Additional Input Types
// ============================================================================

/// Input for creating a Wave product
#[derive(InputObject)]
pub struct CreateWaveProductInput {
    /// Product name
    pub name: String,
    /// Product description
    pub description: Option<String>,
    /// Default unit price
    pub unit_price: f64,
    /// Whether this product is sold (default true)
    pub is_sold: Option<bool>,
    /// Income account ID to link this product to
    pub income_account_id: Option<String>,
}

/// Input for updating a Wave product
#[derive(InputObject)]
pub struct UpdateWaveProductInput {
    /// Wave product ID to update
    pub id: String,
    /// Product name
    pub name: Option<String>,
    /// Product description
    pub description: Option<String>,
    /// Default unit price
    pub unit_price: Option<f64>,
    /// Income account ID to link this product to
    pub income_account_id: Option<String>,
}

/// Input for updating a Wave customer
#[derive(InputObject)]
pub struct UpdateWaveCustomerInput {
    /// Wave customer ID to update
    pub id: String,
    /// Customer name
    pub name: Option<String>,
    /// Customer email
    pub email: Option<String>,
    /// Customer address
    pub address: Option<WaveAddressInput>,
    /// Currency code
    pub currency: Option<String>,
}

/// Input for Wave address
#[derive(InputObject, Clone)]
pub struct WaveAddressInput {
    pub address_line1: Option<String>,
    pub address_line2: Option<String>,
    pub city: Option<String>,
    pub province_code: Option<String>,
    pub postal_code: Option<String>,
    pub country_code: Option<String>,
}
