use super::{
    Installments, MessagesExchanged, OrderId, RiskLevel, SalesChannel, ShippingAmount, TaxAmount,
    TotalAmount, VisitorId,
};
use crate::types::Currency;
use crate::types::address::Address;
use crate::types::customer::Customer;
use crate::types::events::Event;
use crate::types::hotel::Hotel;
use crate::types::orders::triggered::decision_list::TriggeredDecision;
use crate::types::orders::triggered::triggered_rules::TriggeredRules;
use crate::types::payments::Payment;
use crate::types::point_of_sale::PointOfSale;
use crate::types::seller::Seller;
use crate::types::shopping_cart::Product;
use crate::types::travel::Travel;
use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};

/// Pedido completo para análise de fraude
///
/// Ref: <https://docs.konduto.com/reference/enviar-um-pedido>
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Order {
    /// ID único do pedido (obrigatório)
    pub id: OrderId,

    /// ID do visitante obtido do JavaScript (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub visitor: Option<VisitorId>,

    /// Valor total do pedido (obrigatório)
    pub total_amount: TotalAmount,

    /// Valor do frete (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shipping_amount: Option<ShippingAmount>,

    /// Valor dos impostos (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tax_amount: Option<TaxAmount>,

    /// Código da moeda ISO-4217 (opcional, default: BRL)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub currency: Option<Currency>,

    /// Número de parcelas (opcional, min: 1, max: 999)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub installments: Option<Installments>,

    /// Endereço IP do cliente (opcional, IPv4 ou IPv6)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ip: Option<String>,

    /// Data/hora da primeira mensagem em marketplace (opcional, ISO 8601)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub first_message: Option<String>,

    /// Número de mensagens trocadas em marketplace (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub messages_exchanged: Option<MessagesExchanged>,

    /// Data/hora da compra (opcional, ISO 8601)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub purchased_at: Option<String>,

    /// Flag indicando se é pedido recorrente (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recurring: Option<bool>,

    /// Nível de risco do pedido (recomendado)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub risk_level: Option<RiskLevel>,

    /// Se deve ser analisado (opcional, default: true)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub analyze: Option<bool>,

    /// Canal de vendas (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sales_channel: Option<SalesChannel>,

    /// Dados do cliente (obrigatório)
    pub customer: Customer,

    /// Lista de meios de pagamento (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment: Option<Vec<Payment>>,

    /// Endereço de cobrança (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub billing: Option<Address>,

    /// Endereço de entrega (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shipping: Option<Address>,

    /// Lista de itens do carrinho (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shopping_cart: Option<Vec<Product>>,

    /// Dados do hotel (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hotel: Option<Hotel>,

    /// Dados de viagem (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub travel: Option<Travel>,

    /// Dados do vendedor em marketplace (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub seller: Option<Seller>,

    /// Lista de eventos (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub events: Option<Vec<Event>>,

    /// Indica se transação é agendada - PIX/SafeBanking (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scheduled: Option<bool>,

    /// Ponto de venda físico (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub point_of_sale: Option<PointOfSale>,

    /// triggered decision list -> retorna qual lista foi acionado ao enviar o pedido (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub triggered_decision_list: Option<Vec<TriggeredDecision>>,

    /// triggered rules-> retorna lista de regras que foi acionada para o pedido (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub triggered_rules: Option<Vec<TriggeredRules>>,
}

impl Order {
    /// Cria um builder para construir um pedido
    pub fn builder() -> OrderBuilder {
        OrderBuilder::default()
    }
}

/// Builder para criar pedidos
#[derive(Default)]
pub struct OrderBuilder {
    id: Option<OrderId>,
    visitor: Option<VisitorId>,
    total_amount: Option<TotalAmount>,
    shipping_amount: Option<ShippingAmount>,
    tax_amount: Option<TaxAmount>,
    currency: Option<Currency>,
    installments: Option<Installments>,
    ip: Option<String>,
    first_message: Option<String>,
    messages_exchanged: Option<MessagesExchanged>,
    purchased_at: Option<String>,
    recurring: Option<bool>,
    risk_level: Option<RiskLevel>,
    analyze: Option<bool>,
    sales_channel: Option<SalesChannel>,
    customer: Option<Customer>,
    payment: Vec<Payment>,
    billing: Option<Address>,
    shipping: Option<Address>,
    shopping_cart: Vec<Product>,
    hotel: Option<Hotel>,
    travel: Option<Travel>,
    seller: Option<Seller>,
    events: Vec<Event>,
    scheduled: Option<bool>,
    point_of_sale: Option<PointOfSale>,
}

impl OrderBuilder {
    pub fn id(mut self, id: OrderId) -> Self {
        self.id = Some(id);
        self
    }

    pub fn id_str(mut self, id: impl Into<String>) -> Result<Self, ValidationError> {
        self.id = Some(OrderId::try_new(id)?);
        Ok(self)
    }

    pub fn visitor(mut self, visitor: VisitorId) -> Self {
        self.visitor = Some(visitor);
        self
    }

    pub fn visitor_str(mut self, visitor: impl Into<String>) -> Result<Self, ValidationError> {
        self.visitor = Some(VisitorId::try_new(visitor)?);
        Ok(self)
    }

    pub fn total_amount(mut self, amount: TotalAmount) -> Self {
        self.total_amount = Some(amount);
        self
    }

    pub fn total_amount_value(mut self, amount: f64) -> Result<Self, ValidationError> {
        self.total_amount = Some(TotalAmount::new(amount)?);
        Ok(self)
    }

    pub fn shipping_amount(mut self, amount: ShippingAmount) -> Self {
        self.shipping_amount = Some(amount);
        self
    }

    pub fn shipping_amount_value(mut self, amount: f64) -> Result<Self, ValidationError> {
        self.shipping_amount = Some(ShippingAmount::new(amount)?);
        Ok(self)
    }

    pub fn tax_amount(mut self, amount: TaxAmount) -> Self {
        self.tax_amount = Some(amount);
        self
    }

    pub fn tax_amount_value(mut self, amount: f64) -> Result<Self, ValidationError> {
        self.tax_amount = Some(TaxAmount::new(amount)?);
        Ok(self)
    }

    pub fn currency(mut self, currency: Currency) -> Self {
        self.currency = Some(currency);
        self
    }

    pub fn installments(mut self, installments: Installments) -> Self {
        self.installments = Some(installments);
        self
    }

    pub fn installments_value(mut self, installments: u16) -> Result<Self, ValidationError> {
        self.installments = Some(Installments::new(installments)?);
        Ok(self)
    }

    pub fn ip(mut self, ip: impl Into<String>) -> Self {
        self.ip = Some(ip.into());
        self
    }

    pub fn first_message(mut self, first_message: impl Into<String>) -> Self {
        self.first_message = Some(first_message.into());
        self
    }

    pub fn messages_exchanged(mut self, messages: MessagesExchanged) -> Self {
        self.messages_exchanged = Some(messages);
        self
    }

    pub fn messages_exchanged_value(mut self, messages: u32) -> Result<Self, ValidationError> {
        self.messages_exchanged = Some(MessagesExchanged::new(messages)?);
        Ok(self)
    }

    pub fn purchased_at(mut self, purchased_at: impl Into<String>) -> Self {
        self.purchased_at = Some(purchased_at.into());
        self
    }

    pub fn recurring(mut self, recurring: bool) -> Self {
        self.recurring = Some(recurring);
        self
    }

    pub fn risk_level(mut self, risk_level: RiskLevel) -> Self {
        self.risk_level = Some(risk_level);
        self
    }

    pub fn analyze(mut self, analyze: bool) -> Self {
        self.analyze = Some(analyze);
        self
    }

    pub fn sales_channel(mut self, sales_channel: SalesChannel) -> Self {
        self.sales_channel = Some(sales_channel);
        self
    }

    pub fn sales_channel_str(
        mut self,
        sales_channel: impl Into<String>,
    ) -> Result<Self, ValidationError> {
        self.sales_channel = Some(SalesChannel::try_new(sales_channel)?);
        Ok(self)
    }

    pub fn customer(mut self, customer: Customer) -> Self {
        self.customer = Some(customer);
        self
    }

    pub fn add_payment(mut self, payment: Payment) -> Self {
        self.payment.push(payment);
        self
    }

    pub fn payments(mut self, payments: Vec<Payment>) -> Self {
        self.payment = payments;
        self
    }

    pub fn billing(mut self, billing: Address) -> Self {
        self.billing = Some(billing);
        self
    }

    pub fn shipping(mut self, shipping: Address) -> Self {
        self.shipping = Some(shipping);
        self
    }

    pub fn add_product(mut self, product: Product) -> Self {
        self.shopping_cart.push(product);
        self
    }

    pub fn shopping_cart(mut self, shopping_cart: Vec<Product>) -> Self {
        self.shopping_cart = shopping_cart;
        self
    }

    pub fn hotel(mut self, hotel: Hotel) -> Self {
        self.hotel = Some(hotel);
        self
    }

    pub fn travel(mut self, travel: Travel) -> Self {
        self.travel = Some(travel);
        self
    }

    pub fn seller(mut self, seller: Seller) -> Self {
        self.seller = Some(seller);
        self
    }

    pub fn add_event(mut self, event: Event) -> Self {
        self.events.push(event);
        self
    }

    pub fn events(mut self, events: Vec<Event>) -> Self {
        self.events = events;
        self
    }

    pub fn scheduled(mut self, scheduled: bool) -> Self {
        self.scheduled = Some(scheduled);
        self
    }

    pub fn point_of_sale(mut self, point_of_sale: PointOfSale) -> Self {
        self.point_of_sale = Some(point_of_sale);
        self
    }

    pub fn build(self) -> Result<Order, ValidationError> {
        let payment = if self.payment.is_empty() {
            None
        } else {
            Some(self.payment)
        };

        let shopping_cart = if self.shopping_cart.is_empty() {
            None
        } else {
            Some(self.shopping_cart)
        };

        let events = if self.events.is_empty() {
            None
        } else {
            Some(self.events)
        };

        Ok(Order {
            id: self
                .id
                .ok_or_else(|| ValidationError::EmptyField("order_id".to_string()))?,
            visitor: self.visitor,
            total_amount: self
                .total_amount
                .ok_or_else(|| ValidationError::EmptyField("total_amount".to_string()))?,
            shipping_amount: self.shipping_amount,
            tax_amount: self.tax_amount,
            currency: self.currency,
            installments: self.installments,
            ip: self.ip,
            first_message: self.first_message,
            messages_exchanged: self.messages_exchanged,
            purchased_at: self.purchased_at,
            recurring: self.recurring,
            risk_level: self.risk_level,
            analyze: self.analyze,
            sales_channel: self.sales_channel,
            customer: self
                .customer
                .ok_or_else(|| ValidationError::EmptyField("customer".to_string()))?,
            payment,
            billing: self.billing,
            shipping: self.shipping,
            shopping_cart,
            hotel: self.hotel,
            travel: self.travel,
            seller: self.seller,
            events,
            scheduled: self.scheduled,
            point_of_sale: self.point_of_sale,
            triggered_decision_list: None,
            triggered_rules: None,
        })
    }
}

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

    #[test]
    fn test_order_minimal() {
        let customer = Customer::builder()
            .id("CUST-001")
            .unwrap()
            .name("Test Customer")
            .unwrap()
            .email("test@example.com")
            .unwrap()
            .build()
            .unwrap();

        let order = Order::builder()
            .id_str("10000000001")
            .unwrap()
            .total_amount_value(100.00)
            .unwrap()
            .customer(customer)
            .build()
            .unwrap();

        assert_eq!(order.id.as_str(), "10000000001");
        assert_eq!(order.total_amount.value(), 100.00);
    }

    #[test]
    fn test_order_complete() {
        let customer = Customer::builder()
            .id("CUST-001")
            .unwrap()
            .name("Test Customer")
            .unwrap()
            .email("test@example.com")
            .unwrap()
            .build()
            .unwrap();

        let order = Order::builder()
            .id_str("10000000001")
            .unwrap()
            .visitor_str("da39a3ee5e6b4")
            .unwrap()
            .total_amount_value(100.00)
            .unwrap()
            .shipping_amount_value(20.00)
            .unwrap()
            .tax_amount_value(3.45)
            .unwrap()
            .currency(Currency::BRL)
            .installments_value(2)
            .unwrap()
            .ip("189.68.156.100")
            .purchased_at("2018-12-25T12:00:25Z")
            .recurring(true)
            .risk_level(RiskLevel::Low)
            .analyze(true)
            .sales_channel_str("e-commerce")
            .unwrap()
            .customer(customer)
            .build()
            .unwrap();

        assert_eq!(order.id.as_str(), "10000000001");
        assert_eq!(order.total_amount.value(), 100.00);
        assert_eq!(order.shipping_amount.as_ref().unwrap().value(), 20.00);
        assert_eq!(order.installments.as_ref().unwrap().value(), 2);
    }

    #[test]
    fn test_order_missing_id() {
        let customer = Customer::builder()
            .id("CUST-001")
            .unwrap()
            .name("Test")
            .unwrap()
            .email("test@example.com")
            .unwrap()
            .build()
            .unwrap();

        let result = Order::builder()
            .total_amount_value(100.00)
            .unwrap()
            .customer(customer)
            .build();

        assert!(result.is_err());
    }

    #[test]
    fn test_order_missing_total_amount() {
        let customer = Customer::builder()
            .id("CUST-001")
            .unwrap()
            .name("Test")
            .unwrap()
            .email("test@example.com")
            .unwrap()
            .build()
            .unwrap();

        let result = Order::builder()
            .id_str("123")
            .unwrap()
            .customer(customer)
            .build();

        assert!(result.is_err());
    }

    #[test]
    fn test_order_negative_total_amount() {
        let result = TotalAmount::new(-100.00);
        assert!(result.is_err());
    }

    #[test]
    fn test_order_zero_total_amount() {
        let result = TotalAmount::new(0.0);
        assert!(result.is_err());
    }

    #[test]
    fn test_order_missing_customer() {
        let result = Order::builder()
            .id_str("123")
            .unwrap()
            .total_amount_value(100.00)
            .unwrap()
            .build();

        assert!(result.is_err());
    }

    #[test]
    fn test_order_invalid_installments() {
        let result = Installments::new(1000);
        assert!(result.is_err());

        let result2 = Installments::new(0);
        assert!(result2.is_err());
    }

    #[test]
    fn test_order_serialization() {
        let customer = Customer::builder()
            .id("CUST-001")
            .unwrap()
            .name("Test")
            .unwrap()
            .email("test@example.com")
            .unwrap()
            .build()
            .unwrap();

        let order = Order::builder()
            .id_str("10000000001")
            .unwrap()
            .total_amount_value(100.00)
            .unwrap()
            .customer(customer)
            .build()
            .unwrap();

        let json = serde_json::to_string_pretty(&order).unwrap();
        assert!(json.contains("10000000001"));
        assert!(json.contains("100"));
    }

    #[test]
    fn test_installments_display() {
        let cash = Installments::new(1).unwrap();
        assert_eq!(cash.to_string(), "à vista");

        let installment = Installments::new(12).unwrap();
        assert_eq!(installment.to_string(), "12x");
    }

    #[test]
    fn test_messages_exchanged() {
        let messages = MessagesExchanged::new(30).unwrap();
        assert_eq!(messages.value(), 30);
        assert!(messages.has_messages());

        let no_messages = MessagesExchanged::new(0).unwrap();
        assert!(!no_messages.has_messages());
    }
}
