use crate::connections::db_connection;
use crate::models::{FamilyID, Member, MemberID, NewMember};
use crate::schema::members;
use crate::services::{couples_service, photos_service};
use crate::utils::time_utils::time;
use diesel::RunQueryDsl;
use diesel::prelude::*;

/// Create a new family member
pub async fn create(family_id: FamilyID) -> anyhow::Result<Member> {
    db_connection::execute(|conn| {
        let res: Member = diesel::insert_into(members::table)
            .values(&NewMember {
                family_id: family_id.0,
                time_create: time() as i64,
                time_update: time() as i64,
            })
            .get_result(conn)?;

        Ok(res)
    })
}

/// Get the information of a member
pub async fn get_by_id(id: MemberID) -> anyhow::Result<Member> {
    db_connection::execute(|conn| members::table.filter(members::dsl::id.eq(id.0)).first(conn))
}

/// Get all the members of a family
pub async fn get_all_of_family(id: FamilyID) -> anyhow::Result<Vec<Member>> {
    db_connection::execute(|conn| {
        members::table
            .filter(members::dsl::family_id.eq(id.0))
            .get_results(conn)
    })
}

/// Check whether a member with a given id exists or not
pub async fn exists(family_id: FamilyID, member_id: MemberID) -> anyhow::Result<bool> {
    db_connection::execute(|conn| {
        let count: i64 = members::table
            .filter(
                members::id
                    .eq(member_id.0)
                    .and(members::family_id.eq(family_id.0)),
            )
            .count()
            .get_result(conn)?;

        Ok(count != 0)
    })
}

/// Update the information of a member
pub async fn update(member: &mut Member) -> anyhow::Result<()> {
    member.time_update = time() as i64;

    db_connection::execute(|conn| {
        diesel::update(members::dsl::members.filter(members::dsl::id.eq(member.id().0)))
            .set((
                members::dsl::first_name.eq(member.first_name.clone()),
                members::dsl::last_name.eq(member.last_name.clone()),
                members::dsl::birth_last_name.eq(member.birth_last_name.clone()),
                members::dsl::photo_id.eq(member.photo_id().map(|p| p.0)),
                members::dsl::email.eq(member.email.clone()),
                members::dsl::phone.eq(member.phone.clone()),
                members::dsl::address.eq(member.address.clone()),
                members::dsl::city.eq(member.city.clone()),
                members::dsl::postal_code.eq(member.postal_code.clone()),
                members::dsl::country.eq(member.country.clone()),
                members::dsl::sex.eq(member.sex().map(|s| s.to_str().to_string())),
                members::dsl::time_update.eq(member.time_update),
                members::dsl::mother.eq(member.mother().map(|m| m.0)),
                members::dsl::father.eq(member.father().map(|m| m.0)),
                members::dsl::birth_year.eq(member.birth_year),
                members::dsl::birth_month.eq(member.birth_month),
                members::dsl::birth_day.eq(member.birth_day),
                members::dsl::dead.eq(member.dead),
                members::dsl::death_year.eq(member.death_year),
                members::dsl::death_month.eq(member.death_month),
                members::dsl::death_day.eq(member.death_day),
                members::dsl::note.eq(member.note.clone()),
            ))
            .execute(conn)
    })?;

    Ok(())
}

/// Delete a member photo
pub async fn remove_photo(member: &mut Member) -> anyhow::Result<()> {
    match member.photo_id() {
        None => {}
        Some(photo) => {
            photos_service::delete(photo).await?;
            member.set_photo_id(None);
            update(member).await?;
        }
    }

    Ok(())
}

/// Delete a member
pub async fn delete(member: &mut Member) -> anyhow::Result<()> {
    // Remove associated couple
    for mut c in couples_service::get_all_of_member(member.id()).await? {
        // Check if only one person is attached to the couple
        // i.e. if the current member is the last person attached to the couple
        if c.wife().is_some() && c.husband().is_some() {
            continue;
        }

        couples_service::delete(&mut c).await?;
    }

    // Remove mother and father links
    member.set_mother(None);
    member.set_father(None);

    remove_photo(member).await?;

    // Remove the member
    db_connection::execute(|conn| {
        diesel::delete(members::dsl::members.filter(members::dsl::id.eq(member.id().0)))
            .execute(conn)
    })?;

    Ok(())
}

/// Delete all the members of a family
pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> {
    for mut m in get_all_of_family(family_id).await? {
        delete(&mut m).await?;
    }
    Ok(())
}

pub mod loop_detection {
    use crate::models::{Member, MemberID};
    use std::collections::HashMap;

    #[derive(Debug)]
    struct LoopStack<'a> {
        curr: MemberID,
        prev: Option<&'a LoopStack<'a>>,
    }

    impl LoopStack<'_> {
        pub fn contains(&self, id: MemberID) -> bool {
            if let Some(ls) = &self.prev
                && ls.contains(id) {
                    return true;
                }

            self.curr == id
        }
    }

    fn detect_loop_control(
        members: &HashMap<MemberID, &&Member>,
        curr_stack: &LoopStack,
        next: Option<MemberID>,
    ) -> bool {
        match next {
            None => false,
            Some(id) => {
                if curr_stack.contains(id) {
                    log::debug!("Loop detected! {curr_stack:?}");
                    return true;
                }

                detect_loop_recurse(
                    members,
                    &LoopStack {
                        curr: id,
                        prev: Some(curr_stack),
                    },
                )
            }
        }
    }

    /// Recurse loop detection
    fn detect_loop_recurse(members: &HashMap<MemberID, &&Member>, curr_stack: &LoopStack) -> bool {
        let member = match members.get(&curr_stack.curr) {
            Some(m) => m,
            None => {
                log::warn!(
                    "Member {:?} not found in the tree for loop detection, this should never happen!",
                    curr_stack.curr
                );
                return false;
            }
        };

        detect_loop_control(members, curr_stack, member.mother())
            || detect_loop_control(members, curr_stack, member.father())
    }

    /// Detect loops in members hierarchy
    pub fn detect_loop(members: &[&Member]) -> bool {
        let mut map = HashMap::new();
        for m in members {
            map.insert(m.id(), m);
        }

        for m in members {
            if detect_loop_recurse(
                &map,
                &LoopStack {
                    curr: m.id(),
                    prev: None,
                },
            ) {
                return true;
            }
        }

        false
    }
}
