use nostr_sdk::prelude::*;
use windmark_titanesque::{context::RouteContext, response::Response};

use askama::Template;
use http::Uri;
use mdiu::{Block, Gemtext, Markup};
use rust_i18n::t;
use std::collections::HashSet;
use urlencoding::{decode, encode};

use crate::aska::WindTemplate;
use crate::emoji::*;
use crate::nostru::notes_filter;
use crate::rendering;
use crate::rendering::NoteRenderOptions;
use crate::routes::{gem, resp_error_generic, resp_nocert};
use crate::user::CaracalUser;

#[derive(Template, Clone)]
#[template(path = "hashtag_notes.gmi", escape = "txt")]
struct HashTagContentTemplate {
    req_url: Url,
    hashtag: String,
    note_blocks: Vec<Block>,
}

/// Lists all known hashtags
pub async fn hashtags_index(
    _ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Response {
    let filter = Filter::new().kind(Kind::TextNote);

    let Ok(events) = user.client.database().query(filter).await else {
        return Response::temporary_failure("db error");
    };

    let mut tags: HashSet<String> = HashSet::new();
    for event in events.iter() {
        tags.extend(event.tags.hashtags().map(str::to_string));
    }

    let mut sorted_hashtags = tags.into_iter().collect::<Vec<_>>();
    sorted_hashtags.sort();

    let mut document = vec![];

    for hashtag in sorted_hashtags {
        if hashtag.is_empty() {
            continue;
        }

        match Uri::builder()
            .path_and_query(format!("/hashtags/{}/notes", encode(&hashtag)))
            .build()
        {
            Ok(tag_uri) => {
                document.push(rendering::lnk(tag_uri, Some(hashtag)))
            }
            Err(error) => {
                eprintln!("Cannot build URI for tag {}: {}", hashtag, error)
            }
        }
    }

    Response::success(<Gemtext>::markup(&document))
}

/// Lists all the notes tagged with a hashtag
pub async fn notes_by_hashtag(
    ctx: RouteContext,
    user: &'static mut CaracalUser,
) -> Response {
    let params = ctx.parameters.clone();

    let Ok(hashtag) = decode(params.get("hashtag").unwrap()) else {
        return Response::temporary_failure("Cannot decode tag");
    };

    let filter = notes_filter().limit(1000).hashtag(&*hashtag);

    let _ = user.client.subscribe(filter.clone(), None).await;

    let Ok(events) = user.client.database().query(filter).await else {
        return resp_nocert();
    };

    let Ok(note_blocks) = rendering::render_notes(
        events,
        &user.client,
        None,
        None,
        NoteRenderOptions::default(),
    )
    .await
    else {
        return resp_error_generic();
    };

    Response::success(WindTemplate::render(HashTagContentTemplate {
        req_url: ctx.url,
        hashtag: hashtag.to_string(),
        note_blocks,
    }))
}
