use crate::sdk::endpoints::Endpoints;
use crate::sdk::orders::http::responses::create::CreateAnalysisResponse;
use crate::{KondutoClient, KondutoResult, Order};

impl KondutoClient {
    /// Envia um pedido para análise
    ///
    /// # Argumentos
    ///
    /// * `order` - Dados do pedido a ser analisado
    ///
    /// # Exemplo
    ///
    /// ```no_run
    /// use konduto::{KondutoClient, Order, Customer};
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     use konduto::{OrderId, TotalAmount};
    /// let client = KondutoClient::new()?;
    ///
    ///     let order = Order::builder()
    ///         .id(OrderId::try_new("ORDER123")?)
    ///         .total_amount(TotalAmount::new(100.0)?)
    ///         .build()?;
    ///
    ///     let response = client.analyze(&order).await;
    ///
    ///     match response {
    ///         Ok(order_response) => println!("Pedido analisado com sucesso: {:?}", order_response.order.recommendation),
    ///         Err(err) => println!("Erro ao analisar pedido: {:?}", err)
    ///     }
    ///
    ///     Ok(())
    /// }
    /// ```
    pub async fn analyze(&self, order: &Order) -> KondutoResult<CreateAnalysisResponse> {
        let endpoint = Endpoints::CreateOrderV1.to_string();
        self.post(endpoint.as_str(), order).await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::sdk::decision_list::http::responses::create::StatusResponse;
    use crate::supports::tests::{TestContext, assert_api_success};
    use crate::{
        AnalysisLayerStatus, Customer, DecisionEntryType, DecisionType, OrderId, OrderStatus,
        TotalAmount, TriggeredDecision, TriggeredRules,
    };

    #[tokio::test]
    pub async fn should_predict_order_and_result_should_be_approve()
    -> Result<(), Box<dyn std::error::Error>> {
        let ctx = TestContext::new().await;
        let customer = Customer::builder()
            .id("ord-002")?
            .email("dummy@mail.com")?
            .name("Dummy Customer")?
            .build()?;

        let order_request = Order::builder()
            .id(OrderId::try_new("ord-005")?)
            .total_amount(TotalAmount::new(100.0)?)
            .customer(customer)
            .build()?;

        let response = assert_api_success(ctx.client.analyze(&order_request).await);
        assert_eq!(response.status, StatusResponse::Ok);
        Ok(())
    }

    #[tokio::test]
    pub async fn should_predict_order_and_result_should_be_decline_by_trigged_rules()
    -> Result<(), Box<dyn std::error::Error>> {
        let ctx = TestContext::new().await;
        let customer = Customer::builder()
            .id("ord-002")?
            .email("dummy@mail.com")?
            .name("Dummy Customer")?
            .build()?;

        let order_request = Order::builder()
            .id(OrderId::try_new("ord-005")?)
            .total_amount(TotalAmount::new(151.0)?)
            .customer(customer)
            .build()?;

        let response = assert_api_success(ctx.client.analyze(&order_request).await);
        assert_eq!(response.status, StatusResponse::Ok);
        assert_eq!(response.order.status, OrderStatus::Declined);
        assert_eq!(response.order.triggered_decision_list, None);
        assert_eq!(
            response.order.triggered_rules,
            Some(vec![TriggeredRules::new(
                0,
                "regra por valor".to_string(),
                AnalysisLayerStatus::Fraud,
                DecisionType::Decline
            )])
        );
        Ok(())
    }

    #[tokio::test]
    pub async fn should_predict_order_and_result_should_be_decline_by_trigged_decision_list()
    -> Result<(), Box<dyn std::error::Error>> {
        let ctx = TestContext::new().await;
        let customer = Customer::builder()
            .id("ord-002")?
            .email("declined@mail.com")?
            .name("Dummy Customer")?
            .build()?;

        let order_request = Order::builder()
            .id(OrderId::try_new("ord-005")?)
            .total_amount(TotalAmount::new(10.0)?)
            .customer(customer)
            .build()?;

        let response = assert_api_success(ctx.client.analyze(&order_request).await);
        assert_eq!(response.status, StatusResponse::Ok);
        assert_eq!(response.order.status, OrderStatus::Declined);
        assert_eq!(
            response.order.triggered_decision_list,
            Some(vec![TriggeredDecision::new(
                DecisionType::Decline,
                "EMAIL".to_string(),
                DecisionEntryType::Email
            )])
        );
        assert_eq!(response.order.triggered_rules, None);
        Ok(())
    }
}
