use super::PROVIDER_KEY;
use super::StripePaymentProcessor;
use super::find_customer_for_account::find_customer_for_account;
use crate::models::metadata;
use crate::{payments::Error, pbbson::Model};
use std::collections::HashMap;
use stripe_core::customer::CreateCustomer;

pub(super) async fn sync<B: Clone + Send + Sync + ToString>(
    this: &StripePaymentProcessor<B>,
    account: &Model,
    accounts_bucket: B,
) -> Result<Model, Error> {
    // Find existing Stripe customer for account
    match find_customer_for_account(this, account).await {
        Ok(_customer) => return Ok(account.clone()),
        Err(err) => {
            if let Error::NotFound(_) = err {
            } else {
                return Err(err);
            }
        }
    }

    // Create a Stripe customer for account
    let account_id = account.id().map_err(|e| Error::internal(e.to_string()))?;
    let customer = {
        let mut metadata = HashMap::new();
        metadata.insert("accountId".to_string(), account_id.clone());
        let mut req = CreateCustomer::new().metadata(metadata);
        if let Ok(email) = account.get_str("email") {
            req = req.email(email.to_string())
        }
        if let Ok(name) = account.get_str("name") {
            req = req.name(name.to_string());
        }
        req.send(&this.client).await.map_err(|e| {
            log::error!(err:? = e, req:?; "Failed to create Stripe customer");
            Error::from(e)
        })?
    };
    log::info!(account_id, stripe_customer_id = customer.id.as_str(); "Created Stripe customer");

    // Update account metadata with Stripe customer id
    let mut account = account.clone();

    let account_has_changed = {
        if let Some(stripe_customer_id) = metadata::find_string(&account, "metadata", PROVIDER_KEY, "customerId") {
            customer.id.as_str() == stripe_customer_id
        } else {
            metadata::set(
                &mut account,
                "metadata",
                PROVIDER_KEY,
                "customerId",
                customer.id.as_str().into(),
            );
            true
        }
    };
    if account_has_changed {
        let new_account = this
            .config_store
            .update(accounts_bucket, account.clone())
            .await
            .map_err(|e| Error::internal(e.to_string()))?;
        new_account.clone_into(&mut account);
        let account_id = account.id().map_err(|e| Error::internal(e.to_string()))?;
        log::info!(account_id, stripe_customer_id = customer.id.as_str(); "Updated account metadata to reference Stripe customer");
    }

    Ok(account)
}
