use crate::types::delivery::delivery_company::DeliveryCompany;
use crate::types::delivery::delivery_method::DeliveryMethod;
use crate::types::shared::date_iso_8601::DateIso8601;
use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};

/// Dados de entrega
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct Delivery {
    /// Empresa de entrega (max 100 chars)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delivery_company: Option<DeliveryCompany>,

    /// Método de entrega (max 100 chars)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delivery_method: Option<DeliveryMethod>,

    /// Data estimada de envio (formato ISO 8601: YYYY-MM-DD)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub estimated_shipping_date: Option<DateIso8601>,

    /// Data estimada de entrega (formato ISO 8601: YYYY-MM-DD)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub estimated_delivery_date: Option<DateIso8601>,
}

impl Delivery {
    /// Cria um builder para construir dados de entrega
    pub fn builder() -> DeliveryBuilder {
        DeliveryBuilder::default()
    }
}

/// Builder para criar dados de entrega
#[derive(Default, Debug)]
pub struct DeliveryBuilder {
    delivery: Delivery,
}

impl DeliveryBuilder {
    /// Define a empresa de entrega
    pub fn delivery_company(mut self, company: DeliveryCompany) -> Self {
        self.delivery.delivery_company = Some(company);
        self
    }

    /// Define a empresa de entrega a partir de uma string
    pub fn delivery_company_str(
        mut self,
        company: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.delivery.delivery_company = Some(DeliveryCompany::try_new(company)?);
        Ok(self)
    }

    /// Define o método de entrega
    pub fn delivery_method(mut self, method: DeliveryMethod) -> Self {
        self.delivery.delivery_method = Some(method);
        self
    }

    /// Define o método de entrega a partir de uma string
    pub fn delivery_method_str(
        mut self,
        method: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.delivery.delivery_method = Some(DeliveryMethod::try_new(method)?);
        Ok(self)
    }

    /// Define a data estimada de envio
    pub fn estimated_shipping_date(mut self, date: DateIso8601) -> Self {
        self.delivery.estimated_shipping_date = Some(date);
        self
    }

    /// Define a data estimada de envio a partir de uma string
    pub fn estimated_shipping_date_str(
        mut self,
        date: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.delivery.estimated_shipping_date = Some(DateIso8601::new(date)?);
        Ok(self)
    }

    /// Define a data estimada de entrega
    pub fn estimated_delivery_date(mut self, date: DateIso8601) -> Self {
        self.delivery.estimated_delivery_date = Some(date);
        self
    }

    /// Define a data estimada de entrega a partir de uma string
    pub fn estimated_delivery_date_str(
        mut self,
        date: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.delivery.estimated_delivery_date = Some(DateIso8601::new(date)?);
        Ok(self)
    }

    /// Constrói os dados de entrega
    pub fn build(self) -> Delivery {
        self.delivery
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_delivery_builder_all_fields() {
        let delivery = Delivery::builder()
            .delivery_company_str("Correios")
            .unwrap()
            .delivery_method_str("SEDEX")
            .unwrap()
            .estimated_shipping_date_str("2025-12-01")
            .unwrap()
            .estimated_delivery_date_str("2025-12-05")
            .unwrap()
            .build();

        assert!(delivery.delivery_company.is_some());
        assert_eq!(delivery.delivery_company.unwrap().as_str(), "Correios");
        assert!(delivery.delivery_method.is_some());
        assert!(delivery.estimated_shipping_date.is_some());
        assert!(delivery.estimated_delivery_date.is_some());
    }

    #[test]
    fn test_delivery_builder_with_typed_values() {
        let company = DeliveryCompany::try_new("FedEx").unwrap();
        let method = DeliveryMethod::try_new("Express").unwrap();
        let ship_date = DateIso8601::new("2025-11-20").unwrap();
        let delivery_date = DateIso8601::new("2025-11-22").unwrap();

        let delivery = Delivery::builder()
            .delivery_company(company)
            .delivery_method(method)
            .estimated_shipping_date(ship_date)
            .estimated_delivery_date(delivery_date)
            .build();

        assert!(delivery.delivery_company.is_some());
        assert_eq!(
            delivery.delivery_company.as_ref().unwrap().as_str(),
            "FedEx"
        );
        assert_eq!(
            delivery.delivery_method.as_ref().unwrap().as_str(),
            "Express"
        );
    }

    #[test]
    fn test_delivery_builder_partial_fields() {
        let delivery = Delivery::builder()
            .delivery_company_str("DHL")
            .unwrap()
            .estimated_delivery_date_str("2025-12-10")
            .unwrap()
            .build();

        assert!(delivery.delivery_company.is_some());
        assert!(delivery.delivery_method.is_none());
        assert!(delivery.estimated_shipping_date.is_none());
        assert!(delivery.estimated_delivery_date.is_some());
    }

    #[test]
    fn test_delivery_default() {
        let delivery = Delivery::default();

        assert!(delivery.delivery_company.is_none());
        assert!(delivery.delivery_method.is_none());
        assert!(delivery.estimated_shipping_date.is_none());
        assert!(delivery.estimated_delivery_date.is_none());
    }

    #[test]
    fn test_delivery_empty_builder() {
        let delivery = Delivery::builder().build();

        assert!(delivery.delivery_company.is_none());
        assert!(delivery.delivery_method.is_none());
        assert!(delivery.estimated_shipping_date.is_none());
        assert!(delivery.estimated_delivery_date.is_none());
    }

    #[test]
    fn test_delivery_company_validation() {
        let result = Delivery::builder().delivery_company_str("").unwrap_err();

        // Com nutype, o erro vem como Generic (mas a validação funciona)
        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::EmptyField(_)));
    }

    #[test]
    fn test_delivery_company_too_long() {
        let long_company = "a".repeat(101);
        let result = Delivery::builder()
            .delivery_company_str(long_company)
            .unwrap_err();

        // Com nutype, o erro vem como Generic (mas a validação funciona)
        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::TooLong { .. }));
    }

    #[test]
    fn test_delivery_method_validation() {
        let result = Delivery::builder().delivery_method_str("").unwrap_err();

        // Com nutype, o erro vem como Generic (mas a validação funciona)
        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::EmptyField(_)));
    }

    #[test]
    fn test_delivery_method_too_long() {
        let long_method = "a".repeat(101);
        let result = Delivery::builder()
            .delivery_method_str(long_method)
            .unwrap_err();

        // Com nutype, o erro vem como Generic (mas a validação funciona)
        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::TooLong { .. }));
    }

    #[test]
    fn test_invalid_shipping_date_format() {
        let result = Delivery::builder()
            .estimated_shipping_date_str("2025/12/01")
            .unwrap_err();

        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
    }

    #[test]
    fn test_invalid_delivery_date_format() {
        let result = Delivery::builder()
            .estimated_delivery_date_str("01-12-2025")
            .unwrap_err();

        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
    }

    #[test]
    fn test_invalid_date_values() {
        let result = Delivery::builder()
            .estimated_shipping_date_str("2025-13-01")
            .unwrap_err();

        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
    }

    #[test]
    fn test_delivery_serialization() {
        let delivery = Delivery::builder()
            .delivery_company_str("Correios")
            .unwrap()
            .delivery_method_str("PAC")
            .unwrap()
            .estimated_shipping_date_str("2025-11-25")
            .unwrap()
            .estimated_delivery_date_str("2025-11-30")
            .unwrap()
            .build();

        let json = serde_json::to_string(&delivery).unwrap();

        assert!(json.contains("Correios"));
        assert!(json.contains("PAC"));
        assert!(json.contains("2025-11-25"));
        assert!(json.contains("2025-11-30"));
    }

    #[test]
    fn test_delivery_deserialization() {
        let json = r#"{
            "delivery_company": "UPS",
            "delivery_method": "Ground",
            "estimated_shipping_date": "2025-12-01",
            "estimated_delivery_date": "2025-12-05"
        }"#;

        let delivery: Delivery = serde_json::from_str(json).unwrap();

        assert_eq!(delivery.delivery_company.unwrap().as_str(), "UPS");
        assert_eq!(delivery.delivery_method.unwrap().as_str(), "Ground");
        assert_eq!(
            delivery.estimated_shipping_date.unwrap().as_str(),
            "2025-12-01"
        );
        assert_eq!(
            delivery.estimated_delivery_date.unwrap().as_str(),
            "2025-12-05"
        );
    }

    #[test]
    fn test_delivery_deserialization_partial() {
        let json = r#"{
            "delivery_company": "DHL"
        }"#;

        let delivery: Delivery = serde_json::from_str(json).unwrap();

        assert!(delivery.delivery_company.is_some());
        assert!(delivery.delivery_method.is_none());
        assert!(delivery.estimated_shipping_date.is_none());
        assert!(delivery.estimated_delivery_date.is_none());
    }

    #[test]
    fn test_delivery_serialization_skip_none() {
        let delivery = Delivery::builder()
            .delivery_company_str("FedEx")
            .unwrap()
            .build();

        let json = serde_json::to_string(&delivery).unwrap();

        assert!(json.contains("delivery_company"));
        assert!(!json.contains("delivery_method"));
        assert!(!json.contains("estimated_shipping_date"));
        assert!(!json.contains("estimated_delivery_date"));
    }

    #[test]
    fn test_delivery_roundtrip() {
        let original = Delivery::builder()
            .delivery_company_str("Correios")
            .unwrap()
            .delivery_method_str("SEDEX")
            .unwrap()
            .estimated_shipping_date_str("2025-11-20")
            .unwrap()
            .estimated_delivery_date_str("2025-11-25")
            .unwrap()
            .build();

        let json = serde_json::to_string(&original).unwrap();
        let deserialized: Delivery = serde_json::from_str(&json).unwrap();

        assert_eq!(original, deserialized);
    }

    #[test]
    fn test_delivery_clone() {
        let delivery1 = Delivery::builder()
            .delivery_company_str("DHL")
            .unwrap()
            .build();

        let delivery2 = delivery1.clone();

        assert_eq!(delivery1, delivery2);
    }

    #[test]
    fn test_delivery_equality() {
        let delivery1 = Delivery::builder()
            .delivery_company_str("FedEx")
            .unwrap()
            .delivery_method_str("Express")
            .unwrap()
            .build();

        let delivery2 = Delivery::builder()
            .delivery_company_str("FedEx")
            .unwrap()
            .delivery_method_str("Express")
            .unwrap()
            .build();

        assert_eq!(delivery1, delivery2);
    }

    #[test]
    fn test_delivery_inequality() {
        let delivery1 = Delivery::builder()
            .delivery_company_str("FedEx")
            .unwrap()
            .build();

        let delivery2 = Delivery::builder()
            .delivery_company_str("UPS")
            .unwrap()
            .build();

        assert_ne!(delivery1, delivery2);
    }

    #[test]
    fn test_delivery_debug() {
        let delivery = Delivery::builder()
            .delivery_company_str("Correios")
            .unwrap()
            .build();

        let debug_str = format!("{:?}", delivery);
        assert!(debug_str.contains("Delivery"));
    }

    #[test]
    fn test_common_brazilian_companies() {
        let companies = vec!["Correios", "Jadlog", "Loggi", "Total Express"];

        for company_name in companies {
            let delivery = Delivery::builder()
                .delivery_company_str(company_name)
                .unwrap()
                .build();

            assert_eq!(delivery.delivery_company.unwrap().as_str(), company_name);
        }
    }

    #[test]
    fn test_common_delivery_methods() {
        let methods = vec!["SEDEX", "PAC", "Express", "Ground", "Standard"];

        for method_name in methods {
            let delivery = Delivery::builder()
                .delivery_method_str(method_name)
                .unwrap()
                .build();

            assert_eq!(delivery.delivery_method.unwrap().as_str(), method_name);
        }
    }

    #[test]
    fn test_delivery_with_same_date() {
        let delivery = Delivery::builder()
            .estimated_shipping_date_str("2025-12-01")
            .unwrap()
            .estimated_delivery_date_str("2025-12-01")
            .unwrap()
            .build();

        assert_eq!(
            delivery.estimated_shipping_date.unwrap().as_str(),
            delivery.estimated_delivery_date.unwrap().as_str()
        );
    }

    #[test]
    fn test_delivery_date_validation_edge_cases() {
        // Último dia do mês
        let delivery = Delivery::builder()
            .estimated_shipping_date_str("2025-01-31")
            .unwrap()
            .build();
        assert!(delivery.estimated_shipping_date.is_some());

        // Fevereiro em ano não bissexto
        let delivery = Delivery::builder()
            .estimated_shipping_date_str("2025-02-28")
            .unwrap()
            .build();
        assert!(delivery.estimated_shipping_date.is_some());

        // Fevereiro em ano bissexto
        let delivery = Delivery::builder()
            .estimated_shipping_date_str("2024-02-29")
            .unwrap()
            .build();
        assert!(delivery.estimated_shipping_date.is_some());
    }

    #[test]
    fn test_delivery_invalid_february_29_non_leap_year() {
        let result = Delivery::builder()
            .estimated_shipping_date_str("2025-02-29")
            .unwrap_err();

        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
    }

    #[test]
    fn test_delivery_company_with_special_chars() {
        let delivery = Delivery::builder()
            .delivery_company_str("DHL Express (Brasil)")
            .unwrap()
            .build();

        assert_eq!(
            delivery.delivery_company.unwrap().as_str(),
            "DHL Express (Brasil)"
        );
    }

    #[test]
    fn test_delivery_method_with_special_chars() {
        let delivery = Delivery::builder()
            .delivery_method_str("Next-Day Air")
            .unwrap()
            .build();

        assert_eq!(delivery.delivery_method.unwrap().as_str(), "Next-Day Air");
    }
}
