use chrono_tz::Tz;
use poise::{
    serenity_prelude::{
        CreateActionRow, CreateEmbed, CreateEmbedFooter, CreateSelectMenu, CreateSelectMenuKind,
        CreateSelectMenuOption,
    },
    CreateReply,
};
use serde::{Deserialize, Serialize};

use crate::{
    component_models::{
        pager::{DelPager, Pager},
        ComponentDataModel, DelSelector,
    },
    consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR},
    models::{reminder::Reminder, CtxData},
    utils::{Extract, Recordable},
    Context, Error,
};

pub fn max_delete_page(reminders: &[Reminder], timezone: &Tz) -> usize {
    let mut rows = 0;
    let mut char_count = 0;

    reminders
        .iter()
        .enumerate()
        .map(|(count, reminder)| reminder.display_del(count, timezone))
        .fold(1, |mut pages, reminder| {
            rows += 1;
            char_count += reminder.len();

            if char_count > EMBED_DESCRIPTION_MAX_LENGTH || rows > SELECT_MAX_ENTRIES {
                rows = 1;
                char_count = reminder.len();
                pages += 1;
            }

            pages
        })
}

pub fn show_delete_page(reminders: &[Reminder], page: usize, timezone: Tz) -> CreateReply {
    let pager = DelPager::new(page, timezone);

    if reminders.is_empty() {
        let embed = CreateEmbed::new()
            .title("Delete Reminders")
            .description("No Reminders")
            .color(*THEME_COLOR);

        return CreateReply::default().embed(embed).components(vec![pager.create_button_row(0)]);
    }

    let pages = max_delete_page(reminders, &timezone);

    let mut page = page;
    if page >= pages {
        page = pages - 1;
    }

    let mut char_count = 0;
    let mut rows = 0;
    let mut skipped_rows = 0;
    let mut skipped_char_count = 0;
    let mut first_num = 0;

    let mut skipped_pages = 0;

    let (shown_reminders, display_vec): (Vec<&Reminder>, Vec<String>) = reminders
        .iter()
        .enumerate()
        .map(|(count, reminder)| (reminder, reminder.display_del(count, &timezone)))
        .skip_while(|(_, p)| {
            first_num += 1;
            skipped_rows += 1;
            skipped_char_count += p.len();

            if skipped_char_count > EMBED_DESCRIPTION_MAX_LENGTH
                || skipped_rows > SELECT_MAX_ENTRIES
            {
                skipped_rows = 1;
                skipped_char_count = p.len();
                skipped_pages += 1;
            }

            skipped_pages < page
        })
        .take_while(|(_, p)| {
            rows += 1;
            char_count += p.len();

            char_count < EMBED_DESCRIPTION_MAX_LENGTH && rows <= SELECT_MAX_ENTRIES
        })
        .unzip();

    let display = display_vec.join("\n");

    let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone });

    let embed = CreateEmbed::new()
        .title("Delete Reminders")
        .description(display)
        .footer(CreateEmbedFooter::new(format!("Page {} of {}", page + 1, pages)))
        .color(*THEME_COLOR);

    let select_menu = CreateSelectMenu::new(
        del_selector.to_custom_id(),
        CreateSelectMenuKind::String {
            options: shown_reminders
                .iter()
                .enumerate()
                .map(|(count, reminder)| {
                    let c = reminder.display_content();
                    let description = if c.len() > 100 {
                        format!(
                            "{}...",
                            reminder.display_content().chars().take(97).collect::<String>()
                        )
                    } else {
                        c.to_string()
                    };

                    CreateSelectMenuOption::new(
                        (count + first_num).to_string(),
                        reminder.id.to_string(),
                    )
                    .description(description)
                })
                .collect(),
        },
    );

    CreateReply::default()
        .embed(embed)
        .components(vec![pager.create_button_row(pages), CreateActionRow::SelectMenu(select_menu)])
}

#[derive(Serialize, Deserialize, Extract)]
pub struct Options;

impl Recordable for Options {
    async fn run(self, ctx: Context<'_>) -> Result<(), Error> {
        let timezone = ctx.timezone().await;

        let reminders =
            Reminder::from_guild(&ctx, &ctx.data().database, ctx.guild_id(), ctx.author().id).await;

        let resp = show_delete_page(&reminders, 0, timezone);

        ctx.send(resp).await?;

        Ok(())
    }
}

/// Delete reminders
#[poise::command(
    slash_command,
    rename = "delete",
    identifying_name = "delete",
    default_member_permissions = "MANAGE_GUILD"
)]
pub async fn command(ctx: Context<'_>) -> Result<(), Error> {
    (Options {}).run(ctx).await
}
