use axum::extract::State;
use common::database::sdk_data::DbSdkAccountRow;
use serde::Deserialize;

use crate::{
    database::{self, DbError, SelectSdkAccount},
    sdk_util, SdkState,
};

use super::{fail_json, json, post, Json, Router, Value};

pub fn routes() -> Router<SdkState> {
    Router::new()
        .route("/:product_name/mdk/shield/api/login", post(login))
        .route("/:product_name/mdk/shield/api/verify", post(verify))
}

#[derive(Deserialize)]
struct LoginRequest {
    pub account: String,
    pub password: String,
    pub is_crypto: bool,
}

#[derive(Deserialize)]
struct VerifyRequest {
    pub uid: String,
    pub token: String,
}

async fn login(state: State<SdkState>, request: Json<LoginRequest>) -> Json<Value> {
    if !request.is_crypto {
        return fail_json(
            -10,
            "Invalid account format: unencrypted passwords are disabled by SDK security policy",
        );
    }

    let Ok(password) = sdk_util::rsa_decrypt(&request.password) else {
        return fail_json(-10, "Your patch is outdated, get a new one at https://discord.gg/reversedrooms (Password decryption failed)");
    };

    let account = match database::select_sdk_account(
        &state.pg_pool,
        SelectSdkAccount::ByUsername(request.account.clone()),
    )
    .await
    {
        Ok(account) => account,
        Err(DbError::NotFound) => return fail_json(-101, "Account or password error"),
        Err(DbError::SqlxError(err)) => {
            tracing::error!("database error: {err}");
            return fail_json(-1, "Internal server error");
        }
    };

    if !account.password.verify(&password) {
        return fail_json(-101, "Account or password error");
    }

    success_json(account)
}

async fn verify(state: State<SdkState>, request: Json<VerifyRequest>) -> Json<Value> {
    let Ok(uid) = request.uid.parse::<i32>() else {
        return fail_json(-101, "Account cache error");
    };

    let account =
        match database::select_sdk_account(&state.pg_pool, SelectSdkAccount::ByUid(uid)).await {
            Ok(account) => account,
            Err(DbError::NotFound) => return fail_json(-101, "Account cache error"),
            Err(DbError::SqlxError(err)) => {
                tracing::error!("SQL error: {err}");
                return fail_json(-1, "Internal server error");
            }
        };

    if account.token == request.token {
        success_json(account)
    } else {
        fail_json(-101, "For account safety, please log in again")
    }
}

fn success_json(account: DbSdkAccountRow) -> Json<serde_json::Value> {
    Json(json!({
        "data": {
            "account": {
                "area_code": "**",
                "email": account.username.as_str(),
                "country": "RU",
                "is_email_verify": "1",
                "token": account.token,
                "uid": account.uid.to_string()
            },
            "device_grant_required": false,
            "reactivate_required": false,
            "realperson_required": false,
            "safe_mobile_required": false
        },
        "message": "OK",
        "retcode": 0
    }))
}
