use serde::{Deserialize, Deserializer, Serialize};

use crate::{
    TDataT, TIlosci, TKwotowy, TKwotowy2, TNaturalny, TOznaczenieProcedury, TProcentowy,
    TStawkaPodatku, TZnakowy, TGTU,
};

/// Tolerant deserializer for `tax_rate` — accepts either a JSON number or a
/// string. KSeF FA(3) `P_12` allows non-numeric codes (`"zw"`, `"oo"`,
/// `"np I"`, `"0 KR"`, `"0 WDT"`, `"0 EX"`), and callers frequently serialize
/// percentages as strings (`"23"`). Callers routing a non-numeric code should
/// also populate `p_12` — the XML generator picks it up via the typed field.
fn tolerant_tax_rate<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
    D: Deserializer<'de>,
{
    use serde::de::Error;
    let v = serde_json::Value::deserialize(deserializer)?;
    match v {
        serde_json::Value::Null => Ok(0.0),
        serde_json::Value::Number(n) => Ok(n.as_f64().unwrap_or(0.0)),
        serde_json::Value::String(s) => {
            if s.trim().is_empty() {
                Ok(0.0)
            } else {
                Ok(s.trim().parse::<f64>().unwrap_or(0.0))
            }
        }
        _ => Err(Error::custom("taxRate must be a number or string")),
    }
}

/// Wiersz faktury (FaWiersz).
///
/// Szczegółowe pozycje faktury w walucie, w której wystawiono fakturę.
/// Węzeł opcjonalny dla faktury zaliczkowej, faktury korygującej fakturę
/// zaliczkową oraz faktur korygujących dotyczących wszystkich dostaw towarów
/// lub usług dokonanych lub świadczonych w danym okresie, o których mowa
/// w art. 106j ust. 3 ustawy.
/// minOccurs="0", maxOccurs="10000".
///
/// Keeps the original flat fields (`name`, `quantity`, etc.) for backward compat
/// and adds all FA(3) XSD fields as optional.
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct InvoiceLineItem {
    // ====== Legacy fields (kept for backward compat) ======
    /// Nazwa towaru lub usługi (mapped from P_7).
    pub name: String,
    /// Ilość (mapped from P_8B).
    pub quantity: f64,
    /// Jednostka miary (mapped from P_8A).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unit: Option<String>,
    /// Wartość netto (mapped from P_11).
    pub net_amount: f64,
    /// Stawka podatku procentowo (mapped from P_12). Accepts number or string;
    /// non-numeric codes (`"zw"`, `"oo"`, `"np I"`, `"0 KR"`, `"0 WDT"`,
    /// `"0 EX"`) coerce to 0.0 — caller should also set `p_12` for correctness.
    #[serde(deserialize_with = "tolerant_tax_rate")]
    pub tax_rate: f64,
    /// Kwota podatku (mapped from P_11Vat).
    pub tax_amount: f64,
    /// Wartość brutto (netto + podatek).
    pub gross_amount: f64,
    /// Cena jednostkowa netto (mapped from P_9A).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unit_price: Option<f64>,
    /// Data dostawy/usługi na poziomie wiersza (mapped from P_6A).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub service_date: Option<String>,
    /// Symbol PKWiU.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pkwiu: Option<String>,
    /// Numer GTIN.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub gtin: Option<String>,
    /// Symbol CN.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cn: Option<String>,
    /// Kod GTU.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub gtu: Option<String>,

    // ====== FA(3) XSD fields ======
    /// Kolejny numer wiersza faktury (NrWierszaFa).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub nr_wiersza_fa: Option<TNaturalny>,
    /// Uniwersalny unikalny numer wiersza faktury (UU_ID).
    ///
    /// TZnakowy50. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uu_id: Option<TZnakowy>,
    /// Data dostawy/usługi na poziomie wiersza (P_6A).
    ///
    /// Data dokonania lub zakończenia dostawy towarów lub wykonania usługi
    /// lub data otrzymania zapłaty, o której mowa w art. 106b ust. 1 pkt 4 ustawy,
    /// o ile taka data jest określona i różni się od daty wystawienia faktury.
    /// Pole wypełnia się w przypadku gdy dla poszczególnych pozycji faktury
    /// występują różne daty. TDataT. minOccurs="0".
    #[serde(rename = "p6a", skip_serializing_if = "Option::is_none")]
    pub p_6a: Option<TDataT>,
    /// Nazwa (rodzaj) towaru lub usługi (P_7).
    ///
    /// Pole opcjonalne wyłącznie dla przypadku określonego w art. 106j ust. 3
    /// pkt 2 ustawy (faktura korygująca). TZnakowy512. minOccurs="0".
    #[serde(rename = "p7", skip_serializing_if = "Option::is_none")]
    pub p_7: Option<TZnakowy>,
    /// Wewnętrzny kod towaru lub usługi (Indeks).
    ///
    /// Pole przeznaczone do wpisania wewnętrznego kodu towaru lub usługi
    /// nadanego przez podatnika albo dodatkowego opisu. TZnakowy50. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub indeks: Option<TZnakowy>,
    /// Globalny numer jednostki handlowej (GTIN).
    ///
    /// TZnakowy20. minOccurs="0".
    #[serde(rename = "gtinFa", skip_serializing_if = "Option::is_none")]
    pub gtin_fa: Option<TZnakowy>,
    /// Symbol Polskiej Klasyfikacji Wyrobów i Usług (PKWiU).
    ///
    /// TZnakowy50. minOccurs="0".
    #[serde(rename = "pkwiuFa", skip_serializing_if = "Option::is_none")]
    pub pkwiu_fa: Option<TZnakowy>,
    /// Symbol Nomenklatury Scalonej (CN).
    ///
    /// TZnakowy50. minOccurs="0".
    #[serde(rename = "cnFa", skip_serializing_if = "Option::is_none")]
    pub cn_fa: Option<TZnakowy>,
    /// Symbol Polskiej Klasyfikacji Obiektów Budowlanych (PKOB).
    ///
    /// TZnakowy50. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pkob: Option<TZnakowy>,
    /// Miara dostarczonych towarów lub zakres usług (P_8A).
    ///
    /// Pole opcjonalne dla przypadku określonego w art. 106e ust. 5 pkt 3 ustawy.
    /// minOccurs="0".
    #[serde(rename = "p8a", skip_serializing_if = "Option::is_none")]
    pub p_8a: Option<TZnakowy>,
    /// Ilość dostarczonych towarów lub zakres usług (P_8B).
    ///
    /// Pole opcjonalne dla przypadku określonego w art. 106e ust. 5 pkt 3 ustawy.
    /// TIlosci. minOccurs="0".
    #[serde(rename = "p8b", skip_serializing_if = "Option::is_none")]
    pub p_8b: Option<TIlosci>,
    /// Cena jednostkowa netto (P_9A).
    ///
    /// Cena jednostkowa towaru lub usługi bez kwoty podatku (cena jednostkowa netto).
    /// Pole opcjonalne dla przypadków określonych w art. 106e ust. 2 i 3
    /// oraz ust. 5 pkt 3 ustawy. TKwotowy2. minOccurs="0".
    #[serde(rename = "p9a", skip_serializing_if = "Option::is_none")]
    pub p_9a: Option<TKwotowy2>,
    /// Cena jednostkowa brutto (P_9B).
    ///
    /// Cena wraz z kwotą podatku (cena jednostkowa brutto), w przypadku
    /// zastosowania art. 106e ust. 7 i 8 ustawy. TKwotowy2. minOccurs="0".
    #[serde(rename = "p9b", skip_serializing_if = "Option::is_none")]
    pub p_9b: Option<TKwotowy2>,
    /// Kwota opustów lub obniżek cen (P_10).
    ///
    /// Kwoty wszelkich opustów lub obniżek cen, w tym w formie rabatu z tytułu
    /// wcześniejszej zapłaty, o ile nie zostały one uwzględnione w cenie
    /// jednostkowej netto. TKwotowy2. minOccurs="0".
    #[serde(rename = "p10", skip_serializing_if = "Option::is_none")]
    pub p_10: Option<TKwotowy2>,
    /// Wartość sprzedaży netto (P_11).
    ///
    /// Wartość dostarczonych towarów lub wykonanych usług, objętych transakcją,
    /// bez kwoty podatku (wartość sprzedaży netto). Pole opcjonalne dla przypadków
    /// określonych w art. 106e ust. 2 i 3 oraz ust. 5 pkt 3 ustawy.
    /// TKwotowy. minOccurs="0".
    #[serde(rename = "p11", skip_serializing_if = "Option::is_none")]
    pub p_11: Option<TKwotowy>,
    /// Wartość sprzedaży brutto (P_11A).
    ///
    /// Wartość sprzedaży brutto, w przypadku zastosowania art. 106e ust. 7 i 8
    /// ustawy. TKwotowy. minOccurs="0".
    #[serde(rename = "p11a", skip_serializing_if = "Option::is_none")]
    pub p_11a: Option<TKwotowy>,
    /// Kwota podatku wiersza (P_11Vat).
    ///
    /// Kwota podatku w przypadku, o którym mowa w art. 106e ust. 10 ustawy.
    /// TKwotowy. minOccurs="0".
    #[serde(rename = "p11Vat", skip_serializing_if = "Option::is_none")]
    pub p_11_vat: Option<TKwotowy>,
    /// Stawka podatku (P_12).
    ///
    /// Stawka podatku. Pole opcjonalne dla przypadków określonych w art. 106e
    /// ust. 2, 3, ust. 4 pkt 3 i ust. 5 pkt 3 ustawy.
    /// TStawkaPodatku. minOccurs="0".
    #[serde(rename = "p12", skip_serializing_if = "Option::is_none")]
    pub p_12: Option<TStawkaPodatku>,
    /// Stawka VAT - dział XII rozdział 6a (P_12_XII).
    ///
    /// Stawka podatku od wartości dodanej w przypadku, o którym mowa
    /// w dziale XII w rozdziale 6a ustawy. TProcentowy. minOccurs="0".
    #[serde(rename = "p12Xii", skip_serializing_if = "Option::is_none")]
    pub p_12_xii: Option<TProcentowy>,
    /// Znacznik towaru/usługi z załącznika nr 15 (P_12_Zal_15).
    ///
    /// Znacznik dla towaru lub usługi wymienionych w załączniku nr 15
    /// do ustawy - wartość "1". TWybor1. minOccurs="0".
    #[serde(rename = "p12Zal15", skip_serializing_if = "Option::is_none")]
    pub p_12_zal_15: Option<TProcentowy>,
    /// Kwota podatku akcyzowego (KwotaAkcyzy).
    ///
    /// Kwota podatku akcyzowego zawarta w cenie towaru.
    /// TKwotowy. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub kwota_akcyzy: Option<TKwotowy>,
    /// Oznaczenie GTU (GTU).
    ///
    /// Oznaczenie dotyczące dostawy towarów i świadczenia usług.
    /// TGTU. minOccurs="0".
    #[serde(rename = "gtuFa", skip_serializing_if = "Option::is_none")]
    pub gtu_fa: Option<TGTU>,
    /// Oznaczenie procedury (Procedura).
    ///
    /// Oznaczenie dotyczące procedury. TOznaczenieProcedury. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub procedura: Option<TOznaczenieProcedury>,
    /// Kurs waluty wiersza (KursWaluty).
    ///
    /// Kurs waluty stosowany do wyliczenia kwoty podatku w przypadkach,
    /// o których mowa w dziale VI ustawy. TIlosci. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub kurs_waluty: Option<TIlosci>,
    /// Znacznik stanu przed korektą (StanPrzed).
    ///
    /// Znacznik stanu przed korektą w przypadku faktury korygującej,
    /// gdy korekta dotyczy danych wykazanych w pozycjach faktury
    /// i jest dokonywana w sposób polegający na wykazaniu danych
    /// przed korektą i po korekcie jako osobnych wierszy z odrębną numeracją
    /// oraz w przypadku potwierdzania braku zmiany wartości danej pozycji.
    /// TWybor1. minOccurs="0".
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stan_przed: Option<u8>,
}
