/*
    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 super::format_form_error;
use crate::{auth::hash_password, request_info::RequestInfo, routes::error::MyResult};
use anyhow::anyhow;
use jellycommon::{
    jellyobject::{Object, Path, Tag},
    routes::u_account_settings,
    *,
};
use jellydb::{Filter, Query};
use jellyui::{components::user::UserSettings, tr};
use rocket::{
    FromForm,
    form::{self, Contextual, Form, validate::len},
    get, post,
    response::{Flash, Redirect, content::RawHtml},
};
use std::ops::Range;

#[derive(FromForm)]
pub struct SettingsForm {
    #[field(validate = option_len(4..64))]
    password: Option<String>,
    #[field(validate = option_len(4..32))]
    name: Option<String>,
    theme_accent: Option<u32>,
    theme_preset: Option<String>,
}

fn option_len<'v>(value: &Option<String>, range: Range<usize>) -> form::Result<'v, ()> {
    value.as_ref().map(|v| len(v, range)).unwrap_or(Ok(()))
}

#[get("/account/settings")]
pub fn r_account_settings(ri: RequestInfo) -> MyResult<RawHtml<String>> {
    let user = ri.require_user()?;
    Ok(ri.respond_ui(&UserSettings {
        ri: &ri.render_info(),
        user,
    }))
}

#[post("/account/settings", data = "<form>")]
pub fn r_account_settings_post(
    ri: RequestInfo,
    form: Form<Contextual<SettingsForm>>,
) -> MyResult<Flash<Redirect>> {
    let form = match &form.value {
        Some(v) => v,
        None => {
            return Ok(Flash::error(
                Redirect::to(u_account_settings()),
                format_form_error(form),
            ));
        }
    };

    let mut out = String::new();

    if let Some(password) = &form.password {
        let login = ri
            .require_user()?
            .get(USER_LOGIN)
            .ok_or(anyhow!("user has no login"))?;
        let password = hash_password(login, password);
        update_user(&ri, |user| user.insert(USER_PASSWORD, &password))?;
        out += &*tr(ri.lang, "settings.account.password.changed");
        out += "\n";
    }
    if let Some(name) = &form.name {
        update_user(&ri, |user| user.insert(USER_NAME, name))?;
        out += &*tr(ri.lang, "settings.account.display_name.changed");
        out += "\n";
    }
    if let Some(preset) = &form.theme_preset {
        let tag = Tag::new(
            preset
                .as_bytes()
                .try_into()
                .map_err(|_| anyhow!("invalid theme preset"))?,
        );
        update_user(&ri, |user| user.insert(USER_THEME_PRESET, tag))?;
        out += &*tr(ri.lang, "settings.appearance.theme.changed");
        out += "\n";
    }
    if let Some(accent) = form.theme_accent {
        update_user(&ri, |user| user.insert(USER_THEME_ACCENT, accent))?;
    }
    // if let Some(player_preference) = form.player_preference {
    //     update_user_player_preference(&ri.session, player_preference.0)?;
    //     out += &*tr(ri.lang, "settings.player_preference.changed");
    //     out += "\n";
    // }
    let out = if out.is_empty() {
        tr(ri.lang, "settings.no_change").to_string()
    } else {
        out
    };

    Ok(Flash::success(Redirect::to(u_account_settings()), out))
}

fn update_user(ri: &RequestInfo, update: impl Fn(&Object) -> Box<Object>) -> MyResult<()> {
    let login = ri
        .require_user()?
        .get(USER_LOGIN)
        .ok_or(anyhow!("user has no login"))?;
    ri.state.database.transaction(&mut |txn| {
        let user_row = txn
            .query_single(Query {
                filter: Filter::Match(Path(vec![USER_LOGIN.0]), login.into()),
                ..Default::default()
            })?
            .ok_or(anyhow!("user vanished"))?;

        let user = txn.get(user_row)?.unwrap();
        let new_user = update(&user);
        txn.update(user_row, new_user)?;

        Ok(())
    })?;
    Ok(())
}
