use askama::Template;
use askama_web::WebTemplate;
use axum::extract::{Path, State};
use axum::http::HeaderMap;
use axum::response::{AppendHeaders, IntoResponse, Redirect, Response};
use axum::Form;
use serde::Deserialize;

use crate::auth_guard::AuthUser;
use crate::flash;
use crate::htmx;
use crate::page::PageContext;
use crate::state::AppState;

// --- API response types ---

#[derive(Deserialize)]
pub struct BotApiResponse {
    pub id: String,
    pub name: String,
    pub nick: String,
    pub network_addr: String,
    pub sasl_username: Option<String>,
    pub channels: Vec<String>,
    pub enabled: bool,
    pub status: String,
    pub status_message: Option<String>,
    pub running: bool,
    pub created_at: String,
}

#[derive(Deserialize)]
pub struct ScriptApiResponse {
    pub id: String,
    pub name: String,
    pub source: String,
    pub enabled: bool,
}

#[derive(Deserialize)]
pub struct LogApiResponse {
    pub level: String,
    pub message: String,
    pub created_at: String,
}

#[derive(Deserialize)]
pub struct KvApiResponse {
    pub key: String,
    pub value: String,
}

// --- Form types ---

#[derive(Deserialize)]
pub struct CreateBotForm {
    pub name: String,
    pub nick: String,
    pub network_addr: String,
    pub sasl_username: Option<String>,
    pub sasl_password: Option<String>,
    pub channels: Option<String>,
}

#[derive(Deserialize)]
pub struct UpdateBotForm {
    pub name: String,
    pub nick: String,
    pub network_addr: String,
    pub sasl_username: Option<String>,
    pub sasl_password: Option<String>,
    pub channels: Option<String>,
}

#[derive(Deserialize)]
pub struct CreateScriptForm {
    pub name: String,
    pub source: String,
}

#[derive(Deserialize)]
pub struct UpdateScriptForm {
    pub name: String,
    pub source: String,
    pub enabled: Option<String>,
}

#[derive(Deserialize)]
pub struct KvSetForm {
    pub key: String,
    pub value: String,
}

// --- Templates ---

#[derive(Template, WebTemplate)]
#[template(path = "bots.html")]
pub struct BotsTemplate {
    pub page: PageContext,
    pub bots: Vec<BotApiResponse>,
}

#[derive(Template, WebTemplate)]
#[template(path = "bot_create.html")]
pub struct BotCreateTemplate {
    pub page: PageContext,
}

#[derive(Template, WebTemplate)]
#[template(path = "bot_detail.html")]
pub struct BotDetailTemplate {
    pub page: PageContext,
    pub bot: BotApiResponse,
    pub scripts: Vec<ScriptApiResponse>,
    pub channels_str: String,
}

#[derive(Template, WebTemplate)]
#[template(path = "bot_script_edit.html")]
pub struct BotScriptEditTemplate {
    pub page: PageContext,
    pub bot_id: String,
    pub bot_name: String,
    pub script: Option<ScriptApiResponse>,
}

#[derive(Template, WebTemplate)]
#[template(path = "bot_logs.html")]
pub struct BotLogsTemplate {
    pub page: PageContext,
    pub bot_id: String,
    pub bot_name: String,
    pub logs: Vec<LogApiResponse>,
}

#[derive(Template, WebTemplate)]
#[template(path = "bot_kv.html")]
pub struct BotKvTemplate {
    pub page: PageContext,
    pub bot_id: String,
    pub bot_name: String,
    pub kvs: Vec<KvApiResponse>,
}

// --- Handlers ---

pub async fn list(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
) -> impl IntoResponse {
    let bots: Vec<BotApiResponse> = async {
        let resp = state.http_client
            .get(format!("{}/api/bots?user_id={}", state.bot_url, user.sub))
            .send().await.ok()?;
        if !resp.status().is_success() { return None; }
        resp.json().await.ok()
    }.await.unwrap_or_default();

    let page = PageContext::from_request(&headers, &state.announcement);
    let clear = page.flash.is_some();

    let template = BotsTemplate { page, bots };

    if clear {
        (AppendHeaders([flash::clear_flash()]), template).into_response()
    } else {
        template.into_response()
    }
}

pub async fn create_form(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(_user): AuthUser,
) -> impl IntoResponse {
    let page = PageContext::from_request(&headers, &state.announcement);
    BotCreateTemplate { page }
}

pub async fn create(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Form(form): Form<CreateBotForm>,
) -> Response {
    let channels_str = form.channels.unwrap_or_default();
    let channels_vec: Vec<String> = channels_str
        .split(',')
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty())
        .collect();

    let body = serde_json::json!({
        "user_id": user.sub,
        "plan": user.plan.as_deref().unwrap_or("free"),
        "name": form.name.trim(),
        "nick": form.nick.trim(),
        "network_addr": form.network_addr.trim(),
        "sasl_username": form.sasl_username.filter(|s| !s.trim().is_empty()),
        "sasl_password": form.sasl_password.filter(|s| !s.trim().is_empty()),
        "channels": channels_vec,
    });

    let resp = state
        .http_client
        .post(format!("{}/api/bots", state.bot_url))
        .json(&body)
        .send()
        .await;

    match resp {
        Ok(r) if r.status().is_success() => {
            if let Ok(bot) = r.json::<BotApiResponse>().await {
                if htmx::is_htmx(&headers) {
                    return ([htmx::hx_redirect(&format!("/bots/{}", bot.id))], "").into_response();
                }
                return (
                    AppendHeaders([flash::set_flash("bot_created")]),
                    Redirect::to(&format!("/bots/{}", bot.id)),
                )
                    .into_response();
            }
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots")], "").into_response();
            }
            (AppendHeaders([flash::set_flash("bot_created")]), Redirect::to("/bots")).into_response()
        }
        Ok(r) => {
            let code = if r.status().as_u16() == 403 {
                "bot_limit_reached"
            } else {
                "bot_create_failed"
            };
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots/create")], "").into_response();
            }
            (AppendHeaders([flash::set_flash(code)]), Redirect::to("/bots/create")).into_response()
        }
        Err(e) => {
            tracing::error!("bot api error: {e}");
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots/create")], "").into_response();
            }
            (
                AppendHeaders([flash::set_flash("bot_create_failed")]),
                Redirect::to("/bots/create"),
            )
                .into_response()
        }
    }
}

pub async fn detail(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let bot = match fetch_bot(&state, &id, &user.sub).await {
        Some(b) => b,
        None => {
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots")], "").into_response();
            }
            return (AppendHeaders([flash::set_flash("bot_not_found")]), Redirect::to("/bots"))
                .into_response();
        }
    };

    let scripts: Vec<ScriptApiResponse> = async {
        let resp = state.http_client
            .get(format!(
                "{}/api/bots/{}/scripts?user_id={}",
                state.bot_url, id, user.sub
            ))
            .send().await.ok()?;
        if !resp.status().is_success() { return None; }
        resp.json().await.ok()
    }.await.unwrap_or_default();

    let channels_str = bot.channels.join(", ");

    let page = PageContext::from_request(&headers, &state.announcement);
    let clear = page.flash.is_some();

    let template = BotDetailTemplate {
        page,
        bot,
        scripts,
        channels_str,
    };

    if clear {
        (AppendHeaders([flash::clear_flash()]), template).into_response()
    } else {
        template.into_response()
    }
}

pub async fn update(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
    Form(form): Form<UpdateBotForm>,
) -> Response {
    let channels_str = form.channels.unwrap_or_default();
    let channels_vec: Vec<String> = channels_str
        .split(',')
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty())
        .collect();

    let body = serde_json::json!({
        "user_id": user.sub,
        "name": form.name.trim(),
        "nick": form.nick.trim(),
        "network_addr": form.network_addr.trim(),
        "sasl_username": form.sasl_username.filter(|s| !s.trim().is_empty()),
        "sasl_password": form.sasl_password.filter(|s| !s.trim().is_empty()),
        "channels": channels_vec,
    });

    let resp = state
        .http_client
        .put(format!("{}/api/bots/{}", state.bot_url, id))
        .json(&body)
        .send()
        .await;

    let (flash_code, redirect_to) = match resp {
        Ok(r) if r.status().is_success() => ("bot_updated", format!("/bots/{id}")),
        Ok(_) | Err(_) => ("bot_update_failed", format!("/bots/{id}")),
    };

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&redirect_to)], "").into_response()
    } else {
        (AppendHeaders([flash::set_flash(flash_code)]), Redirect::to(&redirect_to)).into_response()
    }
}

pub async fn delete(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let resp = state
        .http_client
        .delete(format!(
            "{}/api/bots/{}?user_id={}",
            state.bot_url, id, user.sub
        ))
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "bot_deleted",
        _ => "bot_delete_failed",
    };

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect("/bots")], "").into_response()
    } else {
        (AppendHeaders([flash::set_flash(flash_code)]), Redirect::to("/bots")).into_response()
    }
}

pub async fn start(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let resp = state
        .http_client
        .post(format!(
            "{}/api/bots/{}/start?user_id={}",
            state.bot_url, id, user.sub
        ))
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "bot_started",
        _ => "bot_start_failed",
    };

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&format!("/bots/{id}"))], "").into_response()
    } else {
        (
            AppendHeaders([flash::set_flash(flash_code)]),
            Redirect::to(&format!("/bots/{id}")),
        )
            .into_response()
    }
}

pub async fn stop(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let resp = state
        .http_client
        .post(format!(
            "{}/api/bots/{}/stop?user_id={}",
            state.bot_url, id, user.sub
        ))
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "bot_stopped",
        _ => "bot_stop_failed",
    };

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&format!("/bots/{id}"))], "").into_response()
    } else {
        (
            AppendHeaders([flash::set_flash(flash_code)]),
            Redirect::to(&format!("/bots/{id}")),
        )
            .into_response()
    }
}

pub async fn script_create_form(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let bot = match fetch_bot(&state, &id, &user.sub).await {
        Some(b) => b,
        None => {
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots")], "").into_response();
            }
            return (AppendHeaders([flash::set_flash("bot_not_found")]), Redirect::to("/bots"))
                .into_response();
        }
    };

    let page = PageContext::from_request(&headers, &state.announcement);
    BotScriptEditTemplate {
        page,
        bot_id: id,
        bot_name: bot.name,
        script: None,
    }
    .into_response()
}

pub async fn script_create(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
    Form(form): Form<CreateScriptForm>,
) -> Response {
    let body = serde_json::json!({
        "user_id": user.sub,
        "name": form.name.trim(),
        "source": form.source,
    });

    let resp = state
        .http_client
        .post(format!("{}/api/bots/{}/scripts", state.bot_url, id))
        .json(&body)
        .send()
        .await;

    let (flash_code, redirect_to) = match resp {
        Ok(r) if r.status().is_success() => {
            if let Ok(script) = r.json::<ScriptApiResponse>().await {
                (
                    "script_created",
                    format!("/bots/{}/scripts/{}", id, script.id),
                )
            } else {
                ("script_created", format!("/bots/{id}"))
            }
        }
        _ => ("script_create_failed", format!("/bots/{id}/scripts/create")),
    };

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&redirect_to)], "").into_response()
    } else {
        (AppendHeaders([flash::set_flash(flash_code)]), Redirect::to(&redirect_to)).into_response()
    }
}

pub async fn script_edit(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path((id, sid)): Path<(String, String)>,
) -> Response {
    let bot = match fetch_bot(&state, &id, &user.sub).await {
        Some(b) => b,
        None => {
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots")], "").into_response();
            }
            return (AppendHeaders([flash::set_flash("bot_not_found")]), Redirect::to("/bots"))
                .into_response();
        }
    };

    let script: Option<ScriptApiResponse> = async {
        let resp = state.http_client
            .get(format!(
                "{}/api/bots/{}/scripts/{}?user_id={}",
                state.bot_url, id, sid, user.sub
            ))
            .send().await.ok()?;
        if !resp.status().is_success() { return None; }
        resp.json().await.ok()
    }.await;

    if script.is_none() {
        if htmx::is_htmx(&headers) {
            return ([htmx::hx_redirect(&format!("/bots/{id}"))], "").into_response();
        }
        return (
            AppendHeaders([flash::set_flash("bot_not_found")]),
            Redirect::to(&format!("/bots/{id}")),
        )
            .into_response();
    }

    let page = PageContext::from_request(&headers, &state.announcement);
    let clear = page.flash.is_some();

    let template = BotScriptEditTemplate {
        page,
        bot_id: id,
        bot_name: bot.name,
        script,
    };

    if clear {
        (AppendHeaders([flash::clear_flash()]), template).into_response()
    } else {
        template.into_response()
    }
}

pub async fn script_update(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path((id, sid)): Path<(String, String)>,
    Form(form): Form<UpdateScriptForm>,
) -> Response {
    let body = serde_json::json!({
        "user_id": user.sub,
        "name": form.name.trim(),
        "source": form.source,
        "enabled": form.enabled.is_some(),
    });

    let resp = state
        .http_client
        .put(format!(
            "{}/api/bots/{}/scripts/{}",
            state.bot_url, id, sid
        ))
        .json(&body)
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "script_updated",
        _ => "script_update_failed",
    };

    let redirect_to = format!("/bots/{}/scripts/{}", id, sid);

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&redirect_to)], "").into_response()
    } else {
        (AppendHeaders([flash::set_flash(flash_code)]), Redirect::to(&redirect_to)).into_response()
    }
}

pub async fn script_delete(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path((id, sid)): Path<(String, String)>,
) -> Response {
    let resp = state
        .http_client
        .delete(format!(
            "{}/api/bots/{}/scripts/{}?user_id={}",
            state.bot_url, id, sid, user.sub
        ))
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "script_deleted",
        _ => "script_delete_failed",
    };

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&format!("/bots/{id}"))], "").into_response()
    } else {
        (
            AppendHeaders([flash::set_flash(flash_code)]),
            Redirect::to(&format!("/bots/{id}")),
        )
            .into_response()
    }
}

pub async fn logs(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let bot = match fetch_bot(&state, &id, &user.sub).await {
        Some(b) => b,
        None => {
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots")], "").into_response();
            }
            return (AppendHeaders([flash::set_flash("bot_not_found")]), Redirect::to("/bots"))
                .into_response();
        }
    };

    let log_entries: Vec<LogApiResponse> = async {
        let resp = state.http_client
            .get(format!(
                "{}/api/bots/{}/logs?user_id={}",
                state.bot_url, id, user.sub
            ))
            .send().await.ok()?;
        if !resp.status().is_success() { return None; }
        resp.json().await.ok()
    }.await.unwrap_or_default();

    let page = PageContext::from_request(&headers, &state.announcement);

    BotLogsTemplate {
        page,
        bot_id: id,
        bot_name: bot.name,
        logs: log_entries,
    }
    .into_response()
}

pub async fn kv_list(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
) -> Response {
    let bot = match fetch_bot(&state, &id, &user.sub).await {
        Some(b) => b,
        None => {
            if htmx::is_htmx(&headers) {
                return ([htmx::hx_redirect("/bots")], "").into_response();
            }
            return (AppendHeaders([flash::set_flash("bot_not_found")]), Redirect::to("/bots"))
                .into_response();
        }
    };

    let kvs: Vec<KvApiResponse> = async {
        let resp = state.http_client
            .get(format!(
                "{}/api/bots/{}/kv?user_id={}",
                state.bot_url, id, user.sub
            ))
            .send().await.ok()?;
        if !resp.status().is_success() { return None; }
        resp.json().await.ok()
    }.await.unwrap_or_default();

    let page = PageContext::from_request(&headers, &state.announcement);

    BotKvTemplate {
        page,
        bot_id: id,
        bot_name: bot.name,
        kvs,
    }
    .into_response()
}

pub async fn kv_set(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path(id): Path<String>,
    Form(form): Form<KvSetForm>,
) -> Response {
    let body = serde_json::json!({
        "user_id": user.sub,
        "value": form.value,
    });

    let resp = state
        .http_client
        .put(format!(
            "{}/api/bots/{}/kv/{}",
            state.bot_url, id, form.key
        ))
        .json(&body)
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "kv_set",
        _ => "kv_set_failed",
    };

    let redirect_to = format!("/bots/{id}/kv");

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&redirect_to)], "").into_response()
    } else {
        (AppendHeaders([flash::set_flash(flash_code)]), Redirect::to(&redirect_to)).into_response()
    }
}

pub async fn kv_delete(
    State(state): State<AppState>,
    headers: HeaderMap,
    AuthUser(user): AuthUser,
    Path((id, key)): Path<(String, String)>,
) -> Response {
    let resp = state
        .http_client
        .delete(format!(
            "{}/api/bots/{}/kv/{}?user_id={}",
            state.bot_url, id, key, user.sub
        ))
        .send()
        .await;

    let flash_code = match resp {
        Ok(r) if r.status().is_success() => "kv_deleted",
        _ => "kv_delete_failed",
    };

    let redirect_to = format!("/bots/{id}/kv");

    if htmx::is_htmx(&headers) {
        ([htmx::hx_redirect(&redirect_to)], "").into_response()
    } else {
        (AppendHeaders([flash::set_flash(flash_code)]), Redirect::to(&redirect_to)).into_response()
    }
}

// --- Helpers ---

async fn fetch_bot(state: &AppState, id: &str, user_id: &str) -> Option<BotApiResponse> {
    state
        .http_client
        .get(format!(
            "{}/api/bots/{}?user_id={}",
            state.bot_url, id, user_id
        ))
        .send()
        .await
        .ok()
        .filter(|r| r.status().is_success())?
        .json::<BotApiResponse>()
        .await
        .ok()
}
