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

use super::handlers::{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 mut ctx = Context::new();
    ctx.insert("slug", &slug);
    ctx.insert("error", &false);
    ctx.insert("base_url", &format!("/ui/{slug}"));
    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(ref registry) = state.registry else {
        return Redirect::to("/ui/").into_response();
    };
    let Some(ctx) = registry.get(&slug) else {
        return StatusCode::NOT_FOUND.into_response();
    };

    match ctx.verify_key(&form.key) {
        Some(role) => {
            let token = state.sessions.create(slug.clone(), role);
            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 mut tctx = Context::new();
            tctx.insert("slug", &slug);
            tctx.insert("error", &true);
            tctx.insert("base_url", &format!("/ui/{slug}"));
            match render(tera, "pages/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()
}
