use crate::helloasso;
use crate::paheko;
use crate::{
    Config, UserCache,
    get_proxy_from_url, get_auth_client_from_cache, parse_and_get_birthday_year, parse_normalize_phone, normalize_str
};
use crate::sync_paheko::{GeneralizedAnswer, sync_paheko};

use anyhow::Result;
use url::Url;

/// rust how to access inner enum value
#[derive(Debug, PartialEq, Clone, Copy)]
enum HelloassoCustomFieldType {
    Email,
    Address,
    PostalCode,
    City,
    Phone,
    Job,
    Skills,
    Birthday,
    LinkedUserFirstName
}

impl TryFrom<&str> for HelloassoCustomFieldType {
    type Error = ();

    fn try_from(subject: &str) -> Result<Self, Self::Error> {
        match subject {
            "Prénom conjoint" => Ok(HelloassoCustomFieldType::LinkedUserFirstName),
            "ADRESSE" => Ok(HelloassoCustomFieldType::Address),
            "CODE POSTAL" => Ok(HelloassoCustomFieldType::PostalCode),
            "VILLE" => Ok(HelloassoCustomFieldType::City),
            "EMAIL" => Ok(HelloassoCustomFieldType::Email),
            "PROFESSION" => Ok(HelloassoCustomFieldType::Job),
            "TÉLÉPHONE" => Ok(HelloassoCustomFieldType::Phone),
            "DATE DE NAISSANCE" => Ok(HelloassoCustomFieldType::Birthday),
            "CENTRE D'INTÉRÊTS / COMPÉTENCES" => Ok(HelloassoCustomFieldType::Skills),
            _ => Err(())
        }
    }
}

fn read_custom_field(form_answer: &helloasso::FormAnswer, custom_field: HelloassoCustomFieldType) -> Option<String> {
    // FIXME: compute the type directly at deserialization with serde
    form_answer.custom_fields.iter()
        .find(|f| HelloassoCustomFieldType::try_from(f.name.as_str()) == Ok(custom_field))
        .map(|cf| cf.answer.clone())
}


pub async fn sync_helloasso(paheko_client: &paheko::AuthentifiedClient, config: &Config, user_cache: &mut UserCache) -> Result<()> {
    let mut ha_client: helloasso::Client = helloasso::Client::new(helloasso::ClientConfig {
        base_url: Url::parse("https://api.helloasso.com/v5/")
                .expect("Expected valid helloasso API base URL"),
        proxy: get_proxy_from_url(&config.helloasso_proxy)?,
        user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/112.0".to_string()
    });

    let login_payload = helloasso::LoginPayload {
            email: config.helloasso_email.clone(),
            password: config.helloasso_password.clone()
    };
    let auth_client: helloasso::AuthentifiedClient =
        get_auth_client_from_cache(user_cache, &mut ha_client, login_payload).await?;

    let org = auth_client.organization(&config.helloasso_organization_slug);
    let answers = org.get_form_answers(&config.helloasso_form_name).await?;
    
    println!("Got {} answers to the membership form. Processing...", &answers.len());

    use email_address::*;
    fn choose_email(answer: &helloasso::FormAnswer) -> Option<String> {
        read_custom_field(answer, HelloassoCustomFieldType::Email)
            .and_then(|x| {
                if !EmailAddress::is_valid(&x) {
                    None
                } else {
                    Some(x)
                }
            })
            .or(Some(answer.payer_user.email.clone()))
    } 

    let mut generalized_answers: Vec<GeneralizedAnswer> = vec![];
    for answer in answers {
        // eprintln!("Processing answer:");
        let email = choose_email(&answer);
        // eprintln!(" email: {:?}", email);

        let mut generalized_answer = GeneralizedAnswer {
            first_name: Some(normalize_str(answer.user.first_name.clone())),
            last_name: normalize_str(answer.user.last_name.clone()),
            email,
            phone: parse_normalize_phone(read_custom_field(&answer, HelloassoCustomFieldType::Phone)),
            skills: read_custom_field(&answer, HelloassoCustomFieldType::Skills).map(normalize_str),
            address: read_custom_field(&answer, HelloassoCustomFieldType::Address)
                .map(normalize_str)
                .expect("Expected ha answer to have address"),
            postal_code: read_custom_field(&answer, HelloassoCustomFieldType::PostalCode)
                .expect("Expected ha answer to have postalcode"),
            city: read_custom_field(&answer, HelloassoCustomFieldType::City)
                .map(normalize_str)
                .expect("Expected ha answer to have city"),
            country: answer.payer_user.country.clone().trim()[..=1].to_string(), // we expect country code ISO 3166-1 alpha-2
            job: read_custom_field(&answer, HelloassoCustomFieldType::Job).map(normalize_str),
            birth_year: read_custom_field(&answer, HelloassoCustomFieldType::Birthday).and_then(parse_and_get_birthday_year),
            inception_time: answer.order.inception_time,
            reference: format!("HA/{}", answer.id),
            donation_amount: 0,
            subscription_amount: answer.amount,
            membership_mode: serde_json::from_value(serde_json::Value::String(answer.mode.clone()))
                .expect("Expected a membership mode to be valid"),
            linked_user_first_name: read_custom_field(&answer, HelloassoCustomFieldType::LinkedUserFirstName)
        };

        // apply custom user override
        // this particular answer had duplicate phone and email from another answer
        if answer.id == 64756582 {
            generalized_answer.email = None;
            generalized_answer.phone = None;
        }

        generalized_answers.push(generalized_answer);
    }
    println!("Generated GeneralizedAnswers");
    sync_paheko(
        paheko_client,
        config,
        user_cache,
        generalized_answers,
        "512",
        "HelloAsso"
    ).await
}