use super::route_prelude::*;
use crate::routes::response::success_askama;
use std::collections::HashMap;

#[derive(Template, Clone)]
#[template(path = "follow_packs/index.gmi", escape = "txt")]
struct FollowPacksIndexTemplate {
    follow_packs: Events,
}

#[derive(Template, Clone)]
#[template(path = "follow_packs/details.gmi", escape = "txt")]
struct FollowPackTemplate {
    follow_pack: Event,
    metadata_events: HashMap<PublicKey, Event>,
}

pub async fn get_follow_pack(
    ctx: RouteContext,
    user: &'static CaracalUser,
) -> Result<Event, UserError> {
    let identifier = ctx.follow_pack_id().ok_or(UserError::InvalidParams)?;

    user.db_query_first(
        Filter::new()
            .kind(Kind::Custom(39089))
            .identifier(identifier),
    )
    .await
    .map_err(|_| UserError::EventNotFound)
}

/// List all follow packs
pub async fn follow_packs_index(
    _ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Result<Response, UserError> {
    let follow_packs = user
        .db_query(Filter::new().kind(Kind::Custom(39089)))
        .await?;

    for pack in follow_packs.iter() {
        user.sub(
            Filter::new()
                .kind(Kind::Metadata)
                .authors(pack.tags.public_keys().map(|k| k.to_owned())),
        )
        .await?;
    }

    Ok(success_askama!(FollowPacksIndexTemplate { follow_packs }))
}

/// Show wn about a follow pack
pub async fn follow_pack_show(
    ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Result<Response, UserError> {
    let follow_pack = get_follow_pack(ctx, user).await?;

    let mut metadata_events = HashMap::new();
    for pubk in follow_pack.tags.public_keys() {
        if let Ok(event) = user
            .db_query_first(Filter::new().kind(Kind::Metadata).author(*pubk))
            .await
        {
            metadata_events.insert(*pubk, event.to_owned());
        }
    }

    Ok(success_askama!(FollowPackTemplate {
        follow_pack,
        metadata_events
    }))
}

/// Perform an action (follow, unfollow) related on the follow pack
pub async fn follow_pack_action(
    ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Result<Response, UserError> {
    let action = ctx.action();
    let follow_pack = get_follow_pack(ctx, user).await?;

    let mut contact_list =
        user.client.get_contact_list(Duration::from_secs(5)).await?;

    match action {
        Action::Follow => {
            for pubk in follow_pack.tags.public_keys() {
                contact_list.push(Contact::new(*pubk));
            }
        }
        Action::Unfollow => {
            for pubk in follow_pack.tags.public_keys() {
                if let Some(index) =
                    contact_list.iter().position(|c| c.public_key == *pubk)
                {
                    contact_list.remove(index);
                }
            }
        }
        _ => (),
    }

    match user
        .send_builder(EventBuilder::contact_list(contact_list))
        .await
    {
        Ok(_) => {
            let _ = user.tx.send(UserCommand::Resubscribe).await;

            Ok(Response::temporary_redirect("/contacts"))
        }
        Err(err) => Ok(Response::temporary_failure(format!(
            "Could not update contact list: {err}"
        ))),
    }
}
