use super::{StripePaymentProcessor, sync_plan_costs};
use crate::payments::adapters::stripe::find_customer_for_account::find_customer_for_account;
use crate::{payments::Error, pbbson::Model};
use pbbson::bson::{DateTime, doc};
use std::collections::HashMap;
use stripe_checkout::CheckoutSessionMode;
use stripe_checkout::checkout_session::{CreateCheckoutSession, CreateCheckoutSessionLineItems};

pub(super) async fn create<B: Clone + Send + Sync + ToString>(
    this: &StripePaymentProcessor<B>,
    account: Model,
    plan: Model,
    plan_metadata_field: &str,
    client_reference_id: Option<String>,
    success_url: Option<String>,
    cancel_url: Option<String>,
) -> Result<Model, Error> {
    // Prepare line items
    let prices = sync_plan_costs::sync(this, &plan, plan_metadata_field).await?;
    let line_items: Vec<_> = prices
        .iter()
        .map(|price| CreateCheckoutSessionLineItems {
            price: Some(price.id.to_string()),
            quantity: Some(1),
            ..Default::default()
        })
        .collect();

    // Create checkout session
    let customer = find_customer_for_account(this, &account).await?;
    log::debug!("Using customer {}", customer.id);
    let mut metadata = HashMap::new();
    if let Ok(account_id) = account.id() {
        metadata.insert("accountId".to_string(), account_id);
    }
    if let Ok(plan_id) = plan.id() {
        metadata.insert("planId".to_string(), plan_id);
    }
    let mut req = CreateCheckoutSession::new()
        .customer(customer.id.as_str())
        .metadata(metadata)
        .mode(CheckoutSessionMode::Subscription)
        .line_items(line_items);
    if let Some(success_url) = success_url {
        req = req.success_url(success_url);
    }
    if let Some(cancel_url) = cancel_url {
        req = req.cancel_url(cancel_url);
    }
    if let Some(client_reference_id) = client_reference_id {
        req = req.client_reference_id(client_reference_id);
    }
    let checkout_session = match req.send(&this.client).await {
        Ok(checkout_session) => checkout_session,
        Err(e) => {
            log::error!(err:? = e; "Failed creating checkout session");
            return Err(e.into());
        }
    };
    log::debug!("Created checkout session {}", checkout_session.id);

    // Return as a model
    let mut checkout_session_model = Model::from(doc! {
        "id": checkout_session.id.as_str(),
        "mode": checkout_session.mode.as_str(),
        "createdAt": DateTime::from_millis(checkout_session.created * 1000),
        "expiresAt": DateTime::from_millis(checkout_session.expires_at * 1000),
    });
    if let Some(amount_total) = checkout_session.amount_total {
        checkout_session_model.insert("amountTotal", amount_total);
    }
    if let Some(cancel_url) = checkout_session.cancel_url {
        checkout_session_model.insert("cancelUrl", cancel_url);
    }
    if let Some(client_reference_id) = checkout_session.client_reference_id {
        checkout_session_model.insert("clientReferenceId", client_reference_id);
    }
    if let Some(customer) = checkout_session.customer {
        checkout_session_model.insert("customer", customer.id().as_str());
    }
    if let Some(success_url) = checkout_session.success_url {
        checkout_session_model.insert("successUrl", success_url);
    }
    if let Some(url) = checkout_session.url {
        checkout_session_model.insert("url", url);
    }
    Ok(checkout_session_model)
}
