/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2026 metamuffin <metamuffin.org>
*/

use std::str::FromStr;

use crate::{auth::hash_password, request_info::RequestInfo, routes::error::MyResult};
use anyhow::anyhow;
use base64::{Engine, prelude::BASE64_URL_SAFE};
use jellycommon::{
    jellyobject::{ObjectBufferBuilder, Path},
    routes::u_admin_users,
    *,
};
use jellydb::{Filter, Query};
use jellyui::{
    components::admin::{AdminUser, AdminUserList},
    tr,
};
use rand::random;
use rocket::{
    FromForm,
    form::Form,
    get, post,
    response::{Flash, Redirect, content::RawHtml},
};

#[get("/admin/users")]
pub fn r_admin_users(ri: RequestInfo) -> MyResult<RawHtml<String>> {
    ri.require_admin()?;

    let mut users = Vec::new();
    ri.state.database.transaction(&mut |txn| {
        users.clear();
        let rows = txn
            .query(Query::from_str("FILTER Ulgn")?)?
            .collect::<Vec<_>>();
        for row in rows {
            let (row, _) = row?;
            users.push(txn.get(row)?.unwrap());
        }
        Ok(())
    })?;

    Ok(ri.respond_ui(&AdminUserList {
        ri: &ri.render_info(),
        users: &users.iter().map(|u| &**u).collect::<Vec<_>>(),
    }))
}

#[derive(FromForm)]
pub struct NewUser {
    login: String,
}

#[post("/admin/new_user", data = "<form>")]
pub fn r_admin_new_user(ri: RequestInfo, form: Form<NewUser>) -> MyResult<Flash<Redirect>> {
    ri.require_admin()?;

    let password = BASE64_URL_SAFE.encode([(); 12].map(|()| random()));
    let password_hashed = hash_password(&form.login, &password);

    ri.state.database.transaction(&mut |txn| {
        let mut user = ObjectBufferBuilder::default();
        user.push(USER_LOGIN, &form.login);
        user.push(USER_PASSWORD, &password_hashed);
        user.push(USER_PASSWORD_REQUIRE_CHANGE, ());
        txn.insert(user.finish())?;
        Ok(())
    })?;

    Ok(Flash::success(
        Redirect::to(u_admin_users()),
        format!("User created; password: {password}"),
    ))
}

#[get("/admin/user/<name>")]
pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<RawHtml<String>> {
    ri.require_admin()?;
    let mut user = None;
    ri.state.database.transaction(&mut |txn| {
        if let Some(row) = txn.query_single(Query {
            filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()),
            ..Default::default()
        })? {
            user = Some(txn.get(row)?.unwrap());
        }
        Ok(())
    })?;
    let Some(user) = user else {
        Err(anyhow!("no such user"))?
    };

    Ok(ri.respond_ui(&AdminUser {
        ri: &ri.render_info(),
        user: &user,
    }))
}

#[post("/admin/user/<name>/remove")]
pub fn r_admin_user_remove(ri: RequestInfo<'_>, name: &str) -> MyResult<Flash<Redirect>> {
    ri.require_admin()?;
    ri.state.database.transaction(&mut |txn| {
        if let Some(row) = txn.query_single(Query {
            filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()),
            ..Default::default()
        })? {
            txn.remove(row)?;
        }
        Ok(())
    })?;
    Ok(Flash::success(
        Redirect::to(u_admin_users()),
        tr(ri.lang, "admin.users.remove_success"),
    ))
}
