use axum::{
    extract::{Form, Path, State},
    http::{header, StatusCode},
    response::{Html, IntoResponse, Redirect, Response},
};
use serde::Deserialize;
use tera::Context;

use super::handlers::{insert_brand_ctx, insert_mcp_ctx, render, UiError};
use crate::api::AppState;
use crate::session::{extract_cookie, COOKIE_NAME};

pub async fn login_page(
    State(state): State<AppState>,
    Path(slug): Path<String>,
) -> Result<Html<String>, UiError> {
    let tera = state.tera.as_ref().ok_or(UiError::NotConfigured)?;
    let base_url = format!("/ui/{slug}");
    let mut ctx = Context::new();
    ctx.insert("slug", &slug);
    ctx.insert("error", &false);
    ctx.insert("base_url", &base_url);
    ctx.insert("hide_project_nav", &true);
    ctx.insert("current_role", "");
    insert_mcp_ctx(&mut ctx, &state.service_flags);
    if let Some(proj) = state.registry.get(&slug) {
        insert_brand_ctx(
            &mut ctx,
            &proj.root,
            &proj.cache,
            proj.import_path.as_deref(),
            &base_url,
            &state.registry,
            &state.service_flags,
        )
        .await;
    }
    render(tera, "pages/login.html", &ctx).await
}

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

pub async fn login_submit(
    State(state): State<AppState>,
    Path(slug): Path<String>,
    Form(form): Form<LoginForm>,
) -> Response {
    let Some(ctx) = state.registry.get(&slug) else {
        return StatusCode::NOT_FOUND.into_response();
    };

    match ctx.verify_key(&form.key) {
        Some(km) => {
            let token = state.sessions.create(slug.clone(), km.role, km.label);
            let cookie = format!(
                "{}={}; Path=/ui/; HttpOnly; SameSite=Strict; Max-Age={}",
                COOKIE_NAME,
                token,
                30 * 24 * 3600,
            );
            (
                [(header::SET_COOKIE, cookie)],
                Redirect::to(&format!("/ui/{slug}/")),
            )
                .into_response()
        }
        None => {
            let tera = match state.tera.as_ref() {
                Some(t) => t,
                None => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
            };
            let base_url = format!("/ui/{slug}");
            let mut tctx = Context::new();
            tctx.insert("slug", &slug);
            tctx.insert("error", &true);
            tctx.insert("base_url", &base_url);
            tctx.insert("hide_project_nav", &true);
            tctx.insert("current_role", "");
            insert_mcp_ctx(&mut tctx, &state.service_flags);
            insert_brand_ctx(
                &mut tctx,
                &ctx.root,
                &ctx.cache,
                ctx.import_path.as_deref(),
                &base_url,
                &state.registry,
                &state.service_flags,
            )
            .await;
            match render(tera, "pages/login.html", &tctx).await {
                Ok(html) => html.into_response(),
                Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
            }
        }
    }
}

pub async fn manage_login_page(State(state): State<AppState>) -> Result<Html<String>, UiError> {
    let tera = state.tera.as_ref().ok_or(UiError::NotConfigured)?;
    let mut ctx = Context::new();
    ctx.insert("error", &false);
    ctx.insert("base_url", "/ui");
    ctx.insert("daemon_admin_enabled", &state.daemon_admin_hash.is_some());
    ctx.insert("hide_project_nav", &true);
    ctx.insert("current_role", "");
    insert_mcp_ctx(&mut ctx, &state.service_flags);
    render(tera, "pages/manage_login.html", &ctx).await
}

pub async fn manage_login_submit(
    State(state): State<AppState>,
    Form(form): Form<LoginForm>,
) -> Response {
    let Some(ref hash) = state.daemon_admin_hash else {
        return StatusCode::NOT_FOUND.into_response();
    };

    let key_entry = crate::registry::KeyEntry {
        role: crate::registry::Role::Admin,
        hash: hash.clone(),
        label: String::new(),
    };
    if crate::registry::verify_keys_list(std::slice::from_ref(&key_entry), &form.key).is_some() {
        let token = state.sessions.create(
            "_daemon".to_string(),
            crate::registry::Role::Admin,
            "daemon-admin".to_string(),
        );
        let cookie = format!(
            "{}={}; Path=/ui/; HttpOnly; SameSite=Strict; Max-Age={}",
            COOKIE_NAME,
            token,
            30 * 24 * 3600,
        );
        ([(header::SET_COOKIE, cookie)], Redirect::to("/ui/manage")).into_response()
    } else {
        let tera = match state.tera.as_ref() {
            Some(t) => t,
            None => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        };
        let mut tctx = Context::new();
        tctx.insert("error", &true);
        tctx.insert("base_url", "/ui");
        tctx.insert("daemon_admin_enabled", &true);
        tctx.insert("hide_project_nav", &true);
        tctx.insert("current_role", "");
        insert_mcp_ctx(&mut tctx, &state.service_flags);
        match render(tera, "pages/manage_login.html", &tctx).await {
            Ok(html) => html.into_response(),
            Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        }
    }
}

pub async fn logout(
    State(state): State<AppState>,
    Path(slug): Path<String>,
    request: axum::extract::Request,
) -> Response {
    let cookie_str = request
        .headers()
        .get(header::COOKIE)
        .and_then(|v| v.to_str().ok())
        .unwrap_or("");

    if let Some(token) = extract_cookie(cookie_str, COOKIE_NAME) {
        state.sessions.revoke(&token);
    }

    let clear = format!(
        "{}=; Path=/ui/; HttpOnly; SameSite=Strict; Max-Age=0",
        COOKIE_NAME
    );
    (
        [(header::SET_COOKIE, clear)],
        Redirect::to(&format!("/ui/{slug}/login")),
    )
        .into_response()
}
