use super::{
    DeliverySlaInMinutes, DeliveryType, ProductCategory, ProductCode, ProductDescription,
    ProductName, Quantity, Sku, UnitCost,
};
use crate::SellerId;
use crate::types::shared::date_iso_8601::DateIso8601;
use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};

/// Produto do carrinho de compras
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Product {
    pub sku: Sku,
    pub product_code: ProductCode,
    pub category: ProductCategory,
    pub name: ProductName,
    pub description: ProductDescription,
    pub unit_cost: UnitCost,
    pub quantity: Quantity,
    pub created_at: DateIso8601,
    #[serde(rename = "deliveryType")]
    pub delivery_type: DeliveryType,
    #[serde(rename = "deliverySlaInMinutes")]
    pub delivery_sla_in_minutes: DeliverySlaInMinutes,
    #[serde(rename = "sellerId")]
    pub seller_id: SellerId,
}

impl Product {
    pub fn builder() -> ProductBuilder {
        ProductBuilder::default()
    }
}

/// Builder para criar produtos
#[derive(Default, Debug)]
pub struct ProductBuilder {
    sku: Option<Sku>,
    product_code: Option<ProductCode>,
    category: Option<ProductCategory>,
    name: Option<ProductName>,
    description: Option<ProductDescription>,
    unit_cost: Option<UnitCost>,
    quantity: Option<Quantity>,
    created_at: Option<DateIso8601>,
    delivery_type: Option<DeliveryType>,
    delivery_sla_in_minutes: Option<DeliverySlaInMinutes>,
    seller_id: Option<SellerId>,
}

impl ProductBuilder {
    /// Define o SKU
    pub fn sku(mut self, sku: Sku) -> Self {
        self.sku = Some(sku);
        self
    }

    /// Define o SKU a partir de uma string
    pub fn sku_str(mut self, sku: impl Into<String>) -> Result<Self, ValidationError> {
        self.sku = Some(Sku::try_new(sku)?);
        Ok(self)
    }

    /// Define o código do produto
    pub fn product_code(mut self, code: ProductCode) -> Self {
        self.product_code = Some(code);
        self
    }

    /// Define o código do produto a partir de uma string
    pub fn product_code_str(mut self, code: impl Into<String>) -> Result<Self, ValidationError> {
        self.product_code = Some(ProductCode::try_new(code)?);
        Ok(self)
    }

    /// Define a categoria
    pub fn category(mut self, category: ProductCategory) -> Self {
        self.category = Some(category);
        self
    }


    /// Define o nome do produto
    pub fn name(mut self, name: ProductName) -> Self {
        self.name = Some(name);
        self
    }

    /// Define o nome do produto a partir de uma string
    pub fn name_str(mut self, name: impl Into<String>) -> Result<Self, ValidationError> {
        self.name = Some(ProductName::try_new(name)?);
        Ok(self)
    }

    /// Define a descrição
    pub fn description(mut self, description: ProductDescription) -> Self {
        self.description = Some(description);
        self
    }

    /// Define a descrição a partir de uma string
    pub fn description_str(
        mut self,
        description: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.description = Some(ProductDescription::try_new(description)?);
        Ok(self)
    }

    /// Define o custo unitário
    pub fn unit_cost(mut self, cost: UnitCost) -> Self {
        self.unit_cost = Some(cost);
        self
    }

    /// Define o custo unitário a partir de um número
    pub fn unit_cost_value(mut self, value: f64) -> Result<Self, ValidationError> {
        self.unit_cost = Some(UnitCost::new(value)?);
        Ok(self)
    }

    /// Define a quantidade
    pub fn quantity(mut self, quantity: Quantity) -> Self {
        self.quantity = Some(quantity);
        self
    }

    /// Define a quantidade a partir de um número
    pub fn quantity_value(mut self, value: u32) -> Result<Self, ValidationError> {
        self.quantity = Some(Quantity::new(value)?);
        Ok(self)
    }

    /// Define a data de criação
    pub fn created_at(mut self, date: DateIso8601) -> Self {
        self.created_at = Some(date);
        self
    }

    /// Define a data de criação a partir de uma string
    pub fn created_at_str(mut self, date: impl Into<String>) -> Result<Self, ValidationError> {
        self.created_at = Some(DateIso8601::new(date)?);
        Ok(self)
    }

    /// Define o tipo de entrega
    pub fn delivery_type(mut self, delivery_type: DeliveryType) -> Self {
        self.delivery_type = Some(delivery_type);
        self
    }

    /// Define o tipo de entrega a partir de uma string
    pub fn delivery_type_str(
        mut self,
        delivery_type: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.delivery_type = Some(DeliveryType::try_new(delivery_type)?);
        Ok(self)
    }

    /// Define o SLA de entrega
    pub fn delivery_sla_in_minutes(mut self, sla: DeliverySlaInMinutes) -> Self {
        self.delivery_sla_in_minutes = Some(sla);
        self
    }

    /// Define o SLA de entrega a partir de minutos
    pub fn delivery_sla_minutes(mut self, minutes: u32) -> Result<Self, ValidationError> {
        self.delivery_sla_in_minutes = Some(DeliverySlaInMinutes::new(minutes)?);
        Ok(self)
    }

    /// Define o ID do vendedor
    pub fn seller_id(mut self, id: SellerId) -> Self {
        self.seller_id = Some(id);
        self
    }

    /// Define o ID do vendedor a partir de uma string
    pub fn seller_id_str(mut self, id: impl Into<String>) -> Result<Self, ValidationError> {
        self.seller_id = Some(SellerId::try_new(id)?);
        Ok(self)
    }

    /// Constrói o produto
    ///
    /// Retorna erro se algum campo obrigatório não foi definido
    pub fn build(self) -> Result<Product, ValidationError> {
        Ok(Product {
            sku: self
                .sku
                .ok_or_else(|| ValidationError::EmptyField("sku".to_string()))?,
            product_code: self
                .product_code
                .ok_or_else(|| ValidationError::EmptyField("product_code".to_string()))?,
            category: self
                .category
                .ok_or_else(|| ValidationError::EmptyField("category".to_string()))?,
            name: self
                .name
                .ok_or_else(|| ValidationError::EmptyField("name".to_string()))?,
            description: self
                .description
                .ok_or_else(|| ValidationError::EmptyField("description".to_string()))?,
            unit_cost: self
                .unit_cost
                .ok_or_else(|| ValidationError::EmptyField("unit_cost".to_string()))?,
            quantity: self
                .quantity
                .ok_or_else(|| ValidationError::EmptyField("quantity".to_string()))?,
            created_at: self
                .created_at
                .ok_or_else(|| ValidationError::EmptyField("created_at".to_string()))?,
            delivery_type: self
                .delivery_type
                .ok_or_else(|| ValidationError::EmptyField("delivery_type".to_string()))?,
            delivery_sla_in_minutes: self.delivery_sla_in_minutes.ok_or_else(|| {
                ValidationError::EmptyField("delivery_sla_in_minutes".to_string())
            })?,
            seller_id: self
                .seller_id
                .ok_or_else(|| ValidationError::EmptyField("seller_id".to_string()))?,
        })
    }
}

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

    fn create_valid_product() -> Product {
        Product::builder()
            .sku_str("9919023")
            .unwrap()
            .product_code_str("123456789999")
            .unwrap()
            .category(ProductCategory::try_from(201).unwrap())
            .name_str("Camiseta Verde")
            .unwrap()
            .description_str("Camiseta M")
            .unwrap()
            .unit_cost_value(29.99)
            .unwrap()
            .quantity_value(1)
            .unwrap()
            .created_at_str("2008-12-25")
            .unwrap()
            .delivery_type_str("entrega expressa")
            .unwrap()
            .delivery_sla_minutes(50)
            .unwrap()
            .seller_id_str("11")
            .unwrap()
            .build()
            .unwrap()
    }

    #[test]
    fn test_product_builder_all_fields() {
        let product = create_valid_product();

        assert_eq!(product.sku.as_str(), "9919023");
        assert_eq!(product.product_code.as_str(), "123456789999");
        assert_eq!(product.category.value(), 201);
        assert_eq!(product.name.as_str(), "Camiseta Verde");
        assert_eq!(product.description.as_str(), "Camiseta M");
        assert_eq!(product.unit_cost.value(), 29.99);
        assert_eq!(product.quantity.value(), 1);
        assert_eq!(product.created_at.as_str(), "2008-12-25");
        assert_eq!(product.delivery_type.as_str(), "entrega expressa");
        assert_eq!(product.delivery_sla_in_minutes.minutes(), 50);
        assert_eq!(product.seller_id.as_str(), "11");
    }

    #[test]
    fn test_product_builder_with_typed_values() {
        let product = Product::builder()
            .sku(Sku::try_new("TEST-123").unwrap())
            .product_code(ProductCode::try_new("CODE-456").unwrap())
            .category(ProductCategory::try_from(100).unwrap())
            .name(ProductName::try_new("Test Product").unwrap())
            .description(ProductDescription::try_new("Test Description").unwrap())
            .unit_cost(UnitCost::new(99.99).unwrap())
            .quantity(Quantity::new(5).unwrap())
            .created_at(DateIso8601::new("2025-01-01").unwrap())
            .delivery_type(DeliveryType::try_new("standard").unwrap())
            .delivery_sla_in_minutes(DeliverySlaInMinutes::new(120).unwrap())
            .seller_id(SellerId::try_new("SELLER-1").unwrap())
            .build()
            .unwrap();

        assert_eq!(product.sku.as_str(), "TEST-123");
        assert_eq!(product.quantity.value(), 5);
    }

    #[test]
    fn test_product_missing_field() {
        let result = Product::builder().sku_str("TEST").unwrap().build();

        assert!(result.is_err());
    }

    #[test]
    fn test_product_serialization() {
        let product = create_valid_product();
        let json = serde_json::to_string(&product).unwrap();

        assert!(json.contains("9919023"));
        assert!(json.contains("123456789999"));
        assert!(json.contains("201"));
        assert!(json.contains("Camiseta Verde"));
    }

    #[test]
    fn test_product_deserialization() {
        let json = r#"{
            "sku": "9919023",
            "product_code": "123456789999",
            "category": 201,
            "name": "Camiseta Verde",
            "description": "Camiseta M",
            "unit_cost": 29.99,
            "quantity": 1,
            "created_at": "2008-12-25",
            "deliveryType": "entrega expressa",
            "deliverySlaInMinutes": 50,
            "sellerId": "11"
        }"#;

        let product: Product = serde_json::from_str(json).unwrap();

        assert_eq!(product.sku.as_str(), "9919023");
        assert_eq!(product.category.value(), 201);
        assert_eq!(product.unit_cost.value(), 29.99);
    }

    #[test]
    fn test_product_roundtrip() {
        let original = create_valid_product();
        let json = serde_json::to_string(&original).unwrap();
        let deserialized: Product = serde_json::from_str(&json).unwrap();

        assert_eq!(original, deserialized);
    }

    #[test]
    fn test_product_invalid_sku() {
        let result = Product::builder().sku_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_product_invalid_category() {
        let category_result = ProductCategory::try_from(0).unwrap_err();
        assert!(matches!(category_result, ValidationError::InvalidFormat { .. }));
    }

    #[test]
    fn test_product_invalid_unit_cost() {
        let result = Product::builder().unit_cost_value(-10.0).unwrap_err();

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

    #[test]
    fn test_product_invalid_quantity() {
        let result = Product::builder().quantity_value(0).unwrap_err();

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

    #[test]
    fn test_product_invalid_date() {
        let result = Product::builder().created_at_str("2025-13-01").unwrap_err();

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

    #[test]
    fn test_product_invalid_delivery_sla() {
        let result = Product::builder().delivery_sla_minutes(0).unwrap_err();

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

    #[test]
    fn test_product_clone() {
        let product1 = create_valid_product();
        let product2 = product1.clone();

        assert_eq!(product1, product2);
    }

    #[test]
    fn test_product_debug() {
        let product = create_valid_product();
        let debug_str = format!("{:?}", product);

        assert!(debug_str.contains("Product"));
    }

    #[test]
    fn test_product_multiple_quantities() {
        let product = Product::builder()
            .sku_str("BULK-001")
            .unwrap()
            .product_code_str("BULK")
            .unwrap()
            .category(ProductCategory::try_from(100).unwrap())
            .name_str("Bulk Item")
            .unwrap()
            .description_str("Large quantity")
            .unwrap()
            .unit_cost_value(10.0)
            .unwrap()
            .quantity_value(100)
            .unwrap()
            .created_at_str("2025-01-01")
            .unwrap()
            .delivery_type_str("bulk")
            .unwrap()
            .delivery_sla_minutes(1440)
            .unwrap()
            .seller_id_str("BULK-SELLER")
            .unwrap()
            .build()
            .unwrap();

        assert_eq!(product.quantity.value(), 100);
    }

    #[test]
    fn test_product_high_value() {
        let product = Product::builder()
            .sku_str("PREMIUM-001")
            .unwrap()
            .product_code_str("PREM")
            .unwrap()
            .category(ProductCategory::try_from(999).unwrap())
            .name_str("Premium Product")
            .unwrap()
            .description_str("High value item")
            .unwrap()
            .unit_cost_value(9999.99)
            .unwrap()
            .quantity_value(1)
            .unwrap()
            .created_at_str("2025-01-01")
            .unwrap()
            .delivery_type_str("premium")
            .unwrap()
            .delivery_sla_minutes(30)
            .unwrap()
            .seller_id_str("PREMIUM")
            .unwrap()
            .build()
            .unwrap();

        assert_eq!(product.unit_cost.value(), 9999.99);
    }

    #[test]
    fn test_product_name_with_special_chars() {
        let product = Product::builder()
            .sku_str("SPECIAL-001")
            .unwrap()
            .product_code_str("SPEC")
            .unwrap()
            .category(ProductCategory::try_from(201).unwrap())
            .name_str("Camiseta (M) - Verde & Azul")
            .unwrap()
            .description_str("Produto com caracteres especiais: @#$%")
            .unwrap()
            .unit_cost_value(29.99)
            .unwrap()
            .quantity_value(1)
            .unwrap()
            .created_at_str("2025-01-01")
            .unwrap()
            .delivery_type_str("standard")
            .unwrap()
            .delivery_sla_minutes(60)
            .unwrap()
            .seller_id_str("1")
            .unwrap()
            .build()
            .unwrap();

        assert!(product.name.as_str().contains("&"));
    }
}
