use chrono::prelude::{DateTime, Utc};
use serde::Deserialize;
use crate::client::Mpesa;
use crate::constants::{Invoice, InvoiceItem};
use crate::environment::ApiEnvironment;
use crate::errors::{MpesaError, MpesaResult};
#[derive(Clone, Debug, Deserialize)]
pub struct SingleInvoiceResponse {
    #[serde(rename(deserialize = "rescode"))]
    pub response_code: String,
    #[serde(rename(deserialize = "resmsg"))]
    pub response_message: String,
    #[serde(rename(deserialize = "Status_Message"))]
    pub status_message: String,
}
#[derive(Debug)]
pub struct SingleInvoiceBuilder<'mpesa, Env: ApiEnvironment> {
    client: &'mpesa Mpesa<Env>,
    amount: Option<f64>,
    account_reference: Option<&'mpesa str>,
    billed_full_name: Option<&'mpesa str>,
    billed_period: Option<&'mpesa str>,
    billed_phone_number: Option<&'mpesa str>,
    due_date: Option<DateTime<Utc>>,
    external_reference: Option<&'mpesa str>,
    invoice_items: Option<Vec<InvoiceItem<'mpesa>>>,
    invoice_name: Option<&'mpesa str>,
}
impl<'mpesa, Env: ApiEnvironment> SingleInvoiceBuilder<'mpesa, Env> {
    pub fn new(client: &'mpesa Mpesa<Env>) -> SingleInvoiceBuilder<'mpesa, Env> {
        SingleInvoiceBuilder {
            client,
            amount: None,
            account_reference: None,
            billed_full_name: None,
            billed_period: None,
            billed_phone_number: None,
            due_date: None,
            external_reference: None,
            invoice_items: None,
            invoice_name: None,
        }
    }
    pub fn amount<Number: Into<f64>>(
        mut self,
        amount: Number,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.amount = Some(amount.into());
        self
    }
    pub fn account_reference(
        mut self,
        account_refernce: &'mpesa str,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.account_reference = Some(account_refernce);
        self
    }
    pub fn billed_full_name(
        mut self,
        billed_full_name: &'mpesa str,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.billed_full_name = Some(billed_full_name);
        self
    }
    pub fn billed_period(
        mut self,
        billed_period: &'mpesa str,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.billed_period = Some(billed_period);
        self
    }
    pub fn billed_phone_number(
        mut self,
        billed_phone_number: &'mpesa str,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.billed_phone_number = Some(billed_phone_number);
        self
    }
    pub fn due_date(mut self, due_date: DateTime<Utc>) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.due_date = Some(due_date);
        self
    }
    pub fn external_reference(
        mut self,
        external_reference: &'mpesa str,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.external_reference = Some(external_reference);
        self
    }
    pub fn invoice_items(
        mut self,
        invoice_items: Vec<InvoiceItem<'mpesa>>,
    ) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.invoice_items = Some(invoice_items);
        self
    }
    pub fn invoice_name(mut self, invoice_name: &'mpesa str) -> SingleInvoiceBuilder<'mpesa, Env> {
        self.invoice_name = Some(invoice_name);
        self
    }
    #[allow(clippy::unnecessary_lazy_evaluations)]
    pub async fn send(self) -> MpesaResult<SingleInvoiceResponse> {
        let url = format!(
            "{}/v1/billmanager-invoice/single-invoicing",
            self.client.environment.base_url()
        );
        let payload = Invoice {
            amount: self
                .amount
                .ok_or(MpesaError::Message("amount is required"))?,
            account_reference: self
                .account_reference
                .ok_or(MpesaError::Message("account_reference is required"))?,
            billed_full_name: self
                .billed_full_name
                .ok_or(MpesaError::Message("billed_full_name is required"))?,
            billed_period: self
                .billed_period
                .ok_or(MpesaError::Message("billed_period is required"))?,
            billed_phone_number: self
                .billed_phone_number
                .ok_or(MpesaError::Message("billed_phone_number is required"))?,
            due_date: self
                .due_date
                .ok_or(MpesaError::Message("due_date is required"))?,
            external_reference: self
                .external_reference
                .ok_or(MpesaError::Message("external_reference is required"))?,
            invoice_items: self.invoice_items,
            invoice_name: self
                .invoice_name
                .ok_or(MpesaError::Message("invoice_name is required"))?,
        };
        let response = self
            .client
            .http_client
            .post(&url)
            .bearer_auth(self.client.auth().await?)
            .json(&payload)
            .send()
            .await?;
        if response.status().is_success() {
            let value = response.json().await?;
            return Ok(value);
        }
        let value = response.json().await?;
        Err(MpesaError::SingleInvoiceError(value))
    }
}