use crate::constants::{SizeConstraint, StaticConstraints};
use crate::controllers::HttpResult;
use crate::extractors::family_extractor::FamilyInPath;
use crate::extractors::member_extractor::FamilyAndMemberInPath;
use crate::models::{Member, MemberID, PhotoID, Sex};
use crate::services::{members_service, photos_service};
use crate::utils::countries_utils;
use actix_multipart::form::MultipartForm;
use actix_multipart::form::tempfile::TempFile;
use actix_web::{HttpResponse, web};

serde_with::with_prefix!(prefix_birth "birth_");
serde_with::with_prefix!(prefix_death "death_");

#[derive(serde::Deserialize, Clone)]
pub struct RequestDate {
    pub year: Option<i16>,
    pub month: Option<i16>,
    pub day: Option<i16>,
}

impl RequestDate {
    pub fn check(&self) -> bool {
        let c = StaticConstraints::default();

        self.year.map(|y| c.date_year.validate(y)).unwrap_or(true)
            && self.month.map(|y| c.date_month.validate(y)).unwrap_or(true)
            && self.day.map(|y| c.date_day.validate(y)).unwrap_or(true)
    }
}

#[derive(serde::Deserialize, Clone)]
pub struct MemberRequest {
    first_name: Option<String>,
    last_name: Option<String>,
    birth_last_name: Option<String>,
    email: Option<String>,
    phone: Option<String>,
    address: Option<String>,
    city: Option<String>,
    postal_code: Option<String>,
    country: Option<String>,
    sex: Option<Sex>,
    pub mother: Option<MemberID>,
    pub father: Option<MemberID>,
    #[serde(flatten, with = "prefix_birth")]
    birth: Option<RequestDate>,
    #[serde(default)]
    dead: bool,
    #[serde(flatten, with = "prefix_death")]
    death: Option<RequestDate>,
    note: Option<String>,
}

#[derive(thiserror::Error, Debug)]
enum MemberControllerErr {
    #[error("Malformed first name!")]
    MalformedFirstname,
    #[error("Malformed last name!")]
    MalformedLastname,
    #[error("Malformed birth last name!")]
    MalformedBirthLastname,
    #[error("Malformed email address!")]
    MalformedEmailAddress,
    #[error("Invalid email address!")]
    InvalidEmailAddress,
    #[error("Malformed phone number!")]
    MalformedPhoneNumber,
    #[error("Malformed address!")]
    MalformedAddress,
    #[error("Malformed city!")]
    MalformedCity,
    #[error("Malformed postal code!")]
    MalformedPostalCode,
    #[error("Malformed country!")]
    MalformedCountry,
    #[error("Invalid country code!")]
    InvalidCountryCode,
    #[error("Malformed date of birth!")]
    MalformedDateOfBirth,
    #[error("Malformed date of death!")]
    MalformedDateOfDeath,
    #[error("Malformed note!")]
    MalformedNote,
    #[error("Mother does not exists!")]
    MotherNotExisting,
    #[error("Father does not exists!")]
    FatherNotExisting,
    #[error("Mother and father can not be identical!")]
    MotherAndFatherIdentical,
}

fn check_opt_str_val(
    val: &Option<String>,
    c: SizeConstraint,
    err: MemberControllerErr,
) -> anyhow::Result<()> {
    if let Some(v) = val
        && !c.validate(v) {
            return Err(err.into());
        }
    Ok(())
}

fn trim_opt_val(val: &mut Option<String>) {
    if let Some(s) = val {
        *val = Some(s.trim().to_string());
    }

    if val.as_deref() == Some("") {
        *val = None;
    }
}

impl MemberRequest {
    pub async fn to_member(mut self, member: &mut Member) -> anyhow::Result<()> {
        // Trim values before processing
        trim_opt_val(&mut self.first_name);
        trim_opt_val(&mut self.last_name);
        trim_opt_val(&mut self.birth_last_name);
        trim_opt_val(&mut self.email);
        trim_opt_val(&mut self.country);
        trim_opt_val(&mut self.address);
        trim_opt_val(&mut self.city);
        trim_opt_val(&mut self.note);
        trim_opt_val(&mut self.phone);

        let c = StaticConstraints::default();
        check_opt_str_val(
            &self.first_name,
            c.member_first_name,
            MemberControllerErr::MalformedFirstname,
        )?;

        check_opt_str_val(
            &self.last_name,
            c.member_last_name,
            MemberControllerErr::MalformedLastname,
        )?;

        check_opt_str_val(
            &self.birth_last_name,
            c.member_birth_last_name,
            MemberControllerErr::MalformedBirthLastname,
        )?;

        check_opt_str_val(
            &self.email,
            c.member_email,
            MemberControllerErr::MalformedEmailAddress,
        )?;

        if let Some(mail) = &self.email
            && !mailchecker::is_valid(mail) {
                return Err(MemberControllerErr::InvalidEmailAddress.into());
            }

        check_opt_str_val(
            &self.phone,
            c.member_phone,
            MemberControllerErr::MalformedPhoneNumber,
        )?;

        check_opt_str_val(
            &self.address,
            c.member_address,
            MemberControllerErr::MalformedAddress,
        )?;

        check_opt_str_val(
            &self.city,
            c.member_city,
            MemberControllerErr::MalformedCity,
        )?;

        check_opt_str_val(
            &self.postal_code,
            c.member_postal_code,
            MemberControllerErr::MalformedPostalCode,
        )?;

        check_opt_str_val(
            &self.country,
            c.member_country,
            MemberControllerErr::MalformedCountry,
        )?;

        if let Some(c) = &self.country
            && !countries_utils::is_code_valid(c) {
                return Err(MemberControllerErr::InvalidCountryCode.into());
            }

        if let Some(d) = &self.birth
            && !d.check() {
                return Err(MemberControllerErr::MalformedDateOfBirth.into());
            }

        if let Some(d) = &self.death
            && !d.check() {
                return Err(MemberControllerErr::MalformedDateOfDeath.into());
            }

        check_opt_str_val(
            &self.note,
            c.member_note,
            MemberControllerErr::MalformedNote,
        )?;

        if let Some(mother) = self.mother {
            if !members_service::exists(member.family_id(), mother).await? {
                return Err(MemberControllerErr::MotherNotExisting.into());
            }

            if self.mother == self.father {
                return Err(MemberControllerErr::MotherAndFatherIdentical.into());
            }
        }

        if let Some(father) = self.father
            && !members_service::exists(member.family_id(), father).await? {
                return Err(MemberControllerErr::FatherNotExisting.into());
            }

        member.first_name = self.first_name;
        member.last_name = self.last_name;
        member.birth_last_name = self.birth_last_name;
        member.email = self.email;
        member.phone = self.phone;
        member.address = self.address;
        member.city = self.city;
        member.postal_code = self.postal_code;
        member.country = self.country;
        member.set_sex(self.sex);
        member.set_mother(self.mother);
        member.set_father(self.father);

        member.birth_year = self.birth.as_ref().map(|m| m.year).unwrap_or_default();
        member.birth_month = self.birth.as_ref().map(|m| m.month).unwrap_or_default();
        member.birth_day = self.birth.as_ref().map(|m| m.day).unwrap_or_default();

        member.dead = self.dead;
        member.death_year = self.death.as_ref().map(|m| m.year).unwrap_or_default();
        member.death_month = self.death.as_ref().map(|m| m.month).unwrap_or_default();
        member.death_day = self.death.as_ref().map(|m| m.day).unwrap_or_default();

        member.note = self.note;

        Ok(())
    }
}

#[derive(serde::Serialize)]
struct MemberAPI {
    #[serde(flatten)]
    member: Member,
    signed_photo_id: Option<String>,
}

impl MemberAPI {
    pub fn new(member: Member) -> Self {
        Self {
            signed_photo_id: member.photo_id().as_ref().map(PhotoID::to_signed_hash),
            member,
        }
    }
}

/// Create a new family member
pub async fn create(f: FamilyInPath, req: web::Json<MemberRequest>) -> HttpResult {
    let mut member = members_service::create(f.family_id()).await?;

    if let Err(e) = req.0.to_member(&mut member).await {
        log::error!("Failed to apply member information! {e}");
        members_service::delete(&mut member).await?;
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
    }

    if let Err(e) = members_service::update(&mut member).await {
        log::error!("Failed to update member information! {e}");
        members_service::delete(&mut member).await?;
        return Ok(HttpResponse::InternalServerError().finish());
    }

    Ok(HttpResponse::Ok().json(member))
}

/// Get the entire list of members of the family
pub async fn get_all(f: FamilyInPath) -> HttpResult {
    let members = members_service::get_all_of_family(f.family_id()).await?;
    Ok(HttpResponse::Ok().json(members.into_iter().map(MemberAPI::new).collect::<Vec<_>>()))
}

/// Get the information of a single family member
pub async fn get_single(m: FamilyAndMemberInPath) -> HttpResult {
    Ok(HttpResponse::Ok().json(MemberAPI::new(m.to_member())))
}

/// Update a member information
pub async fn update(m: FamilyAndMemberInPath, req: web::Json<MemberRequest>) -> HttpResult {
    let mut member = m.to_member();

    if let Err(e) = req.0.to_member(&mut member).await {
        log::error!("Failed to parse member information {e}!");
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
    }

    // Check for potential loops
    let members = members_service::get_all_of_family(member.family_id()).await?;
    let mut members_ref = Vec::with_capacity(members.len());
    members_ref.push(&member);
    for m in &members {
        if m.id() != member.id() {
            members_ref.push(m);
        }
    }
    if members_service::loop_detection::detect_loop(&members_ref) {
        log::warn!("Membership update rejected due to detected loop!");
        return Ok(HttpResponse::NotAcceptable().json("Loop detected!"));
    }

    members_service::update(&mut member).await?;

    Ok(HttpResponse::Accepted().finish())
}

/// Delete a member
pub async fn delete(m: FamilyAndMemberInPath) -> HttpResult {
    members_service::delete(&mut m.to_member()).await?;
    Ok(HttpResponse::Ok().finish())
}

#[derive(Debug, MultipartForm)]
pub struct UploadPhotoForm {
    #[multipart(rename = "photo")]
    photo: TempFile,
}

/// Upload a new photo for a user
pub async fn set_photo(
    m: FamilyAndMemberInPath,
    MultipartForm(form): MultipartForm<UploadPhotoForm>,
) -> HttpResult {
    let photo = photos_service::finalize_upload(form.photo.into()).await?;
    let mut member = m.to_member();

    members_service::remove_photo(&mut member).await?;

    member.set_photo_id(Some(photo.id()));
    members_service::update(&mut member).await?;

    Ok(HttpResponse::Ok().finish())
}

/// Remove a photo
pub async fn remove_photo(m: FamilyAndMemberInPath) -> HttpResult {
    let mut member = m.to_member();
    members_service::remove_photo(&mut member).await?;

    Ok(HttpResponse::Ok().finish())
}
