use crate::{Context, Error};
use crate::commands::fun::AiChatKey;
use poise::serenity_prelude as serenity;
use serde::{Deserialize, Serialize};
use serenity::prelude::TypeMapKey;
use std::collections::HashMap;
use surrealdb::Surreal;
use surrealdb::engine::remote::ws::Client;

pub struct DbKey;

impl TypeMapKey for DbKey {
    type Value = Surreal<Client>;
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AutoResponse {
    pub response: String,
    pub mention_reply: bool,
}

#[derive(Deserialize)]
struct GuildRecord {
    auto_responses: Option<HashMap<String, AutoResponse>>,
}

#[poise::command(
    slash_command,
    prefix_command,
    guild_only,
    default_member_permissions = "MANAGE_MESSAGES",
    required_permissions = "MANAGE_MESSAGES"
)]
pub async fn auto_response(
    ctx: Context<'_>,
    #[description = "The trigger message"] msg: String,
    #[description = "The response message"] response: String,
    #[description = "Reply to the user? (default: false)"] mention_reply: Option<bool>,
) -> Result<(), Error> {
    let guild_id = ctx
        .guild_id()
        .ok_or_else(|| Error::msg("Guild only command"))?;
    let db = &ctx.data().db;

    let mention_reply = mention_reply.unwrap_or(false);
    let auto_response = AutoResponse {
        response: response.clone(),
        mention_reply,
    };

    let _: Option<serde::de::IgnoredAny> = db
        .update(("guilds", guild_id.to_string()))
        .merge(serde_json::json!({
            "auto_responses": {
                msg.clone(): auto_response
            }
        }))
        .await?;

    ctx.say(format!(
        "Auto-response configured: `{}` -> `{}` (Reply: {})",
        msg, response, mention_reply
    ))
    .await?;

    Ok(())
}

#[poise::command(
    slash_command,
    prefix_command,
    guild_only,
    default_member_permissions = "MANAGE_MESSAGES",
    required_permissions = "MANAGE_MESSAGES"
)]
pub async fn view_auto_responses(ctx: Context<'_>) -> Result<(), Error> {
    let guild_id = ctx
        .guild_id()
        .ok_or_else(|| Error::msg("Guild only command"))?;
    let db = &ctx.data().db;

    let record: Option<GuildRecord> = db.select(("guilds", guild_id.to_string())).await?;

    let mut response = String::new();

    if let Some(record) = record {
        if let Some(responses) = record.auto_responses {
            if responses.is_empty() {
                response = "No auto-responses configured.".to_string();
            } else {
                response.push_str("**Auto-Responses:**\n");
                for (trigger, data) in responses {
                    response.push_str(&format!(
                        "- `{}` -> `{}` (Reply: {})\n",
                        trigger, data.response, data.mention_reply
                    ));
                }
            }
        } else {
            response = "No auto-responses configured.".to_string();
        }
    } else {
        response = "No configuration found for this guild.".to_string();
    }

    ctx.say(response).await?;

    Ok(())
}

#[poise::command(
    slash_command,
    prefix_command,
    guild_only,
    default_member_permissions = "MANAGE_MESSAGES",
    required_permissions = "MANAGE_MESSAGES"
)]
pub async fn delete_auto_response(
    ctx: Context<'_>,
    #[description = "The trigger message to delete"] msg: String,
) -> Result<(), Error> {
    let guild_id = ctx
        .guild_id()
        .ok_or_else(|| Error::msg("Guild only command"))?;
    let db = &ctx.data().db;

    let record: Option<GuildRecord> = db.select(("guilds", guild_id.to_string())).await?;

    if let Some(record) = record {
        if let Some(mut responses) = record.auto_responses {
            if responses.remove(&msg).is_some() {
                let _: Option<serde::de::IgnoredAny> = db
                    .update(("guilds", guild_id.to_string()))
                    .merge(serde_json::json!({
                        "auto_responses": responses
                    }))
                    .await?;
                ctx.say(format!("Auto-response for `{}` deleted.", msg))
                    .await?;
            } else {
                ctx.say(format!("No auto-response found for `{}`.", msg))
                    .await?;
            }
        } else {
            ctx.say("No auto-responses configured.").await?;
        }
    } else {
        ctx.say("No configuration found for this guild.").await?;
    }

    Ok(())
}

#[poise::command(
    slash_command,
    prefix_command,
    guild_only,
    default_member_permissions = "MANAGE_MESSAGES",
    required_permissions = "MANAGE_MESSAGES"
)]
pub async fn edit_auto_response(
    ctx: Context<'_>,
    #[description = "The trigger message to edit"] msg: String,
    #[description = "The new response message"] new_response: Option<String>,
    #[description = "Reply to the user?"] new_mention_reply: Option<bool>,
) -> Result<(), Error> {
    let guild_id = ctx
        .guild_id()
        .ok_or_else(|| Error::msg("Guild only command"))?;
    let db = &ctx.data().db;

    let record: Option<GuildRecord> = db.select(("guilds", guild_id.to_string())).await?;

    if let Some(record) = record {
        if let Some(mut responses) = record.auto_responses {
            let (response_str, mention_reply_bool) = {
                if let Some(data) = responses.get_mut(&msg) {
                    if let Some(response) = new_response {
                        data.response = response;
                    }
                    if let Some(mention_reply) = new_mention_reply {
                        data.mention_reply = mention_reply;
                    }
                    (data.response.clone(), data.mention_reply)
                } else {
                    ctx.say(format!("No auto-response found for `{}`.", msg))
                        .await?;
                    return Ok(());
                }
            };

            let _: Option<serde::de::IgnoredAny> = db
                .update(("guilds", guild_id.to_string()))
                .merge(serde_json::json!({
                    "auto_responses": responses
                }))
                .await?;

            ctx.say(format!(
                "Auto-response for `{}` updated: `{}` (Reply: {})",
                msg, response_str, mention_reply_bool
            ))
            .await?;
        } else {
            ctx.say("No auto-responses configured.").await?;
        }
    } else {
        ctx.say("No configuration found for this guild.").await?;
    }

    Ok(())
}

#[poise::command(slash_command, prefix_command, guild_only)]
pub async fn summary(
    ctx: Context<'_>,
    #[description = "Message to summarize (reply to one or provide message link)"]
    message: Option<serenity::Message>,
) -> Result<(), Error> {
    if let poise::Context::Application(app_ctx) = ctx {
        app_ctx.defer().await?;
    }

    let msg = if let Some(m) = message {
        m
    } else if let poise::Context::Prefix(prefix_ctx) = ctx {
        // For prefix, check if replying to a message
        if let Some(reply) = prefix_ctx.msg.referenced_message.as_ref() {
            (**reply).clone()
        } else {
            ctx.say("You must reply to a message or provide a message to summarize.")
                .await?;
            return Ok(());
        }
    } else {
        ctx.say("You must reply to a message or provide a message to summarize.")
            .await?;
        return Ok(());
    };

    let data = ctx.serenity_context().data.read().await;
    let ai_chat_manager = data.get::<AiChatKey>().cloned().ok_or_else(|| anyhow::anyhow!("AI Chat manager not found"))?;
    let content = msg.content.trim();

    if content.is_empty() {
        ctx.say("The message is empty, nothing to summarize.").await?;
        return Ok(());
    }

    let prompt = format!("Summarize this message in 1-2 sentences: {}", content);
    match ai_chat_manager.query_ollama_direct(&prompt).await {
        Ok(summary) => {
            ctx.say(format!(
                "**Summary of message by {}:**\n{}",
                msg.author.name, summary
            ))
            .await?;
        }
        Err(err) => {
            tracing::error!(error = %err, "Failed to summarize message");
            ctx.say("Failed to generate summary. Try again later.")
                .await?;
        }
    }

    Ok(())
}

pub async fn process_auto_response(
    ctx: &serenity::Context,
    msg: &serenity::Message,
) -> Result<(), Error> {
    if msg.author.bot {
        return Ok(());
    }

    let guild_id = match msg.guild_id {
        Some(id) => id,
        None => return Ok(()),
    };

    let data = ctx.data.read().await;
    let db = match data.get::<DbKey>() {
        Some(db) => db,
        None => {
            tracing::error!("Database connection not found in context data for utility");
            return Ok(());
        }
    };

    let record: Option<GuildRecord> = db.select(("guilds", guild_id.to_string())).await?;

    if let Some(record) = record {
        if let Some(responses) = record.auto_responses {
            if let Some(auto_response) = responses.get(&msg.content) {
                if auto_response.mention_reply {
                    msg.reply(ctx, &auto_response.response).await?;
                } else {
                    msg.channel_id.say(ctx, &auto_response.response).await?;
                }
            }
        }
    }

    Ok(())
}
