//! Bank management operations for the Arma 3 server extension.
//!
//! Provides Arma 3 extension commands for player data storage, retrieval, and updates.
//! Handles SQF command mapping and parameter validation.

use arma_rs::{CallContext, Group};
use forge_repositories::RedisBankRepository;
use forge_services::BankService;
use std::sync::LazyLock;

use crate::adapters::ExtensionRedisClient;
use crate::helpers::resolve_uid;
use crate::log::log;

/// Global bank service instance.
///
/// Lazily initialized singleton combining Redis adapter, repository, and service layers.
static BANK_SERVICE: LazyLock<BankService<RedisBankRepository<ExtensionRedisClient>>> =
    LazyLock::new(|| {
        let redis_client = ExtensionRedisClient::new();
        let repository = RedisBankRepository::new(redis_client);
        BankService::new(repository)
    });

/// Creates the Arma 3 command group for bank operations.
///
/// Registers commands: `get`, `exists`, `create`, `update`, `delete`.
pub fn group() -> Group {
    Group::new()
        .command("get", get_bank)
        .command("create", create_bank)
        .command("update", update_bank)
        .command("exists", bank_exists)
        .command("delete", delete_bank)
}

/// Retrieves an bank by key/UID.
///
/// Resolves the key to a Steam UID and returns the bank as JSON.
/// Returns an error message if resolution fails or retrieval fails.
pub fn get_bank(call_context: CallContext, key: String) -> String {
    log("bank", "DEBUG", &format!("Getting bank for key: {}", key));

    let resolved_uid = match resolve_uid(&key, &call_context) {
        Some(uid) => {
            log("bank", "DEBUG", &format!("Resolved UID: {}", uid));
            uid
        }
        None => {
            let error_msg = format!("Error: Failed to resolve UID for key: {}", key);
            log("bank", "ERROR", &error_msg);
            return error_msg;
        }
    };

    match BANK_SERVICE.get_bank(resolved_uid.clone()) {
        Ok(bank) => {
            log(
                "bank",
                "INFO",
                &format!("Successfully retrieved bank: {}", resolved_uid),
            );
            match serde_json::to_string(&bank) {
                Ok(json) => {
                    log(
                        "bank",
                        "DEBUG",
                        &format!("Serialized bank to JSON: {}", json),
                    );
                    json
                }
                Err(e) => {
                    let error_msg = format!("Error: Failed to serialize bank: {}", e);
                    log("bank", "ERROR", &error_msg);
                    error_msg
                }
            }
        }
        Err(e) => {
            let error_msg = format!("Error: {}", e);
            log(
                "bank",
                "ERROR",
                &format!("Failed to get bank '{}': {}", resolved_uid, e),
            );
            error_msg
        }
    }
}

/// Creates a new bank with the provided JSON data.
///
/// Resolves key to UID, validates JSON data, and persists the new bank.
pub fn create_bank(call_context: CallContext, key: String, json_data: String) -> String {
    log(
        "bank",
        "DEBUG",
        &format!("Creating bank for key: {} with data: {}", key, json_data),
    );

    let resolved_uid = match resolve_uid(&key, &call_context) {
        Some(uid) => {
            log(
                "bank",
                "DEBUG",
                &format!("Resolved UID for creation: {}", uid),
            );
            uid
        }
        None => {
            let error_msg = format!("Error: Failed to resolve UID for key: {}", key);
            log("bank", "ERROR", &error_msg);
            return error_msg;
        }
    };

    match BANK_SERVICE.create(resolved_uid.clone(), json_data) {
        Ok(bank) => {
            log(
                "bank",
                "INFO",
                &format!("Successfully created bank: {}", resolved_uid),
            );
            match serde_json::to_string(&bank) {
                Ok(json) => {
                    log(
                        "bank",
                        "DEBUG",
                        &format!("Serialized bank to JSON: {}", json),
                    );
                    json
                }
                Err(e) => {
                    let error_msg = format!("Error: Failed to serialize bank: {}", e);
                    log("bank", "ERROR", &error_msg);
                    error_msg
                }
            }
        }
        Err(e) => {
            let error_msg = format!("Error: {}", e);
            log(
                "bank",
                "ERROR",
                &format!("Failed to create bank '{}': {}", resolved_uid, e),
            );
            error_msg
        }
    }
}

/// Updates an existing bank with JSON data.
///
/// Resolves key to UID, applies partial updates from JSON, and persists changes.
pub fn update_bank(call_context: CallContext, key: String, json_update: String) -> String {
    log(
        "bank",
        "DEBUG",
        &format!("Updating bank for key: {} with data: {}", key, json_update),
    );

    let resolved_uid = match resolve_uid(&key, &call_context) {
        Some(uid) => {
            log(
                "bank",
                "DEBUG",
                &format!("Resolved UID for update: {}", uid),
            );
            uid
        }
        None => {
            let error_msg = format!("Error: Failed to resolve UID for key: {}", key);
            log("bank", "ERROR", &error_msg);
            return error_msg;
        }
    };

    match BANK_SERVICE.update_bank(resolved_uid.clone(), json_update) {
        Ok(bank) => {
            log(
                "bank",
                "INFO",
                &format!("Successfully updated bank: {}", resolved_uid),
            );
            match serde_json::to_string(&bank) {
                Ok(json) => {
                    log(
                        "bank",
                        "DEBUG",
                        &format!("Serialized updated bank to JSON: {}", json),
                    );
                    json
                }
                Err(e) => {
                    let error_msg = format!("Error: Failed to serialize bank: {}", e);
                    log("bank", "ERROR", &error_msg);
                    error_msg
                }
            }
        }
        Err(e) => {
            let error_msg = format!("Error: {}", e);
            log(
                "bank",
                "ERROR",
                &format!("Failed to update bank '{}': {}", resolved_uid, e),
            );
            error_msg
        }
    }
}

/// Checks if an bank exists in the database.
///
/// Returns "true" if the bank exists, "false" otherwise.
pub fn bank_exists(call_context: CallContext, key: String) -> String {
    log(
        "bank",
        "DEBUG",
        &format!("Checking if bank exists for key: {}", key),
    );

    let resolved_uid = match resolve_uid(&key, &call_context) {
        Some(uid) => {
            log(
                "bank",
                "DEBUG",
                &format!("Resolved UID for existence check: {}", uid),
            );
            uid
        }
        None => {
            log(
                "bank",
                "WARN",
                &format!("Failed to resolve UID for key: {}", key),
            );
            return "false".to_string();
        }
    };

    match BANK_SERVICE.bank_exists(resolved_uid.clone()) {
        Ok(exists) => {
            let result = if exists { "true" } else { "false" };
            log(
                "bank",
                "DEBUG",
                &format!("Bank '{}' exists: {}", resolved_uid, result),
            );
            result.to_string()
        }
        Err(e) => {
            log(
                "bank",
                "ERROR",
                &format!("Failed to check if bank '{}' exists: {}", resolved_uid, e),
            );
            "false".to_string()
        }
    }
}

/// Permanently deletes an bank.
///
/// Resolves key to UID and removes the bank and associated data.
pub fn delete_bank(call_context: CallContext, key: String) -> String {
    log("bank", "DEBUG", &format!("Deleting bank for key: {}", key));

    let resolved_uid = match resolve_uid(&key, &call_context) {
        Some(uid) => {
            log(
                "bank",
                "DEBUG",
                &format!("Resolved UID for deletion: {}", uid),
            );
            uid
        }
        None => {
            let error_msg = format!("Error: Failed to resolve UID for key: {}", key);
            log("bank", "ERROR", &error_msg);
            return error_msg;
        }
    };

    match BANK_SERVICE.delete_bank(resolved_uid.clone()) {
        Ok(_) => {
            log(
                "bank",
                "INFO",
                &format!("Successfully deleted bank: {}", resolved_uid),
            );
            "OK".to_string()
        }
        Err(e) => {
            let error_msg = format!("Error: {}", e);
            log(
                "bank",
                "ERROR",
                &format!("Failed to delete bank '{}': {}", resolved_uid, e),
            );
            error_msg
        }
    }
}
