// src/tui/functions/common/login.rs

use crate::services::auth::AuthClient;
use crate::state::pages::auth::AuthState;
use crate::state::pages::auth::LoginState;
use crate::state::app::state::AppState;
use crate::state::app::buffer::{AppView, BufferState};
use crate::config::storage::storage::{StoredAuthData, save_auth_data};
use crate::ui::handlers::context::DialogPurpose;
use common::proto::komp_ac::auth::LoginResponse;
use canvas::canvas::CanvasState;
use anyhow::{Context, Result};
use tokio::spawn;
use tokio::sync::mpsc;
use tracing::{info, error};

#[derive(Debug)]
pub enum LoginResult {
    Success(LoginResponse),
    Failure(String),
    ConnectionError(String),
}

/// Attempts to log the user in using the provided credentials via gRPC.
/// Updates AuthState and AppState on success or failure.
pub async fn save(
    auth_state: &mut AuthState,
    login_state: &mut LoginState,
    auth_client: &mut AuthClient,
    app_state: &mut AppState,
) -> Result<String> {
    let identifier = login_state.username.clone();
    let password = login_state.password.clone();

    // --- Client-side validation ---
    // Prevent login attempt if the identifier field is empty or whitespace.
    if identifier.trim().is_empty() {
        let error_message = "Username/Email cannot be empty.".to_string();
        app_state.show_dialog(
            "Login Failed",
            &error_message,
            vec!["OK".to_string()],
            DialogPurpose::LoginFailed,
        );
        login_state.error_message = Some(error_message.clone());
        return Err(anyhow::anyhow!(error_message));
    }

    // Clear previous error/dialog state before attempting
    login_state.error_message = None;
    app_state.hide_dialog(); // Hide any previous dialog

    // Call the gRPC login method
    match auth_client.login(identifier.clone(), password).await
        .with_context(|| format!("gRPC login attempt failed for identifier: {}", identifier))
    {
        Ok(response) => {
            // Store authentication details using correct field names
            auth_state.auth_token = Some(response.access_token.clone());
            auth_state.user_id = Some(response.user_id.clone());
            auth_state.role = Some(response.role.clone());
            auth_state.decoded_username = Some(response.username.clone());
            login_state.set_has_unsaved_changes(false);
            login_state.error_message = None;

            // Format the success message using response data
            let success_message = format!(
                "Login Successful!\n\n\
                Username: {}\n\
                User ID: {}\n\
                Role: {}",
                response.username,
                response.user_id,
                response.role
            );

            app_state.show_dialog(
                "Login Success",
                &success_message,
                vec!["Menu".to_string(), "Exit".to_string()],
                DialogPurpose::LoginSuccess,
            );
            login_state.password.clear();
            login_state.username.clear();
            login_state.current_cursor_pos = 0;
            Ok("Login successful, details shown in dialog.".to_string())
        }
        Err(e) => {
            let error_message = format!("{}", e);
            app_state.show_dialog(
                "Login Failed",
                &error_message,
                vec!["OK".to_string()],
                DialogPurpose::LoginFailed,
            );
            login_state.error_message = Some(error_message.clone());
            login_state.set_has_unsaved_changes(true);
            login_state.username.clear();
            login_state.password.clear();
            Err(e)
        }
    }
}

/// Reverts the login form fields to empty and returns to the previous screen (Intro).
pub async fn revert(
    login_state: &mut LoginState,
    _app_state: &mut AppState, // Keep signature consistent if needed elsewhere
) -> String {
    // Clear the input fields
    login_state.username.clear();
    login_state.password.clear();
    login_state.error_message = None;
    login_state.set_has_unsaved_changes(false);
    login_state.login_request_pending = false; // Ensure flag is reset on revert

    "Login reverted".to_string()
}

pub async fn back_to_main(
    login_state: &mut LoginState,
    app_state: &mut AppState,
    buffer_state: &mut BufferState,
) -> String {
    // Clear the input fields
    login_state.username.clear();
    login_state.password.clear();
    login_state.error_message = None;
    login_state.set_has_unsaved_changes(false);
    login_state.login_request_pending = false; // Ensure flag is reset

    // Ensure dialog is hidden if revert is called
    app_state.hide_dialog();

    // Navigation logic
    buffer_state.close_active_buffer();
    buffer_state.update_history(AppView::Intro);

    // Reset focus state
    app_state.ui.focus_outside_canvas = false;
    app_state.focused_button_index= 0;

    "Returned to main menu".to_string()
}

/// Validates input, shows loading, and spawns the login task.
pub fn initiate_login(
    login_state: &LoginState,
    app_state: &mut AppState,
    mut auth_client: AuthClient,
    sender: mpsc::Sender<LoginResult>,
) -> String {
    let username = login_state.username.clone();
    let password = login_state.password.clone();

    // 1. Client-side validation
    if username.trim().is_empty() {
        app_state.show_dialog(
            "Login Failed",
            "Username/Email cannot be empty.",
            vec!["OK".to_string()],
            DialogPurpose::LoginFailed,
        );
        "Username cannot be empty.".to_string()
    } else {
        // 2. Show Loading Dialog
        app_state.show_loading_dialog("Logging In", "Please wait...");

        // 3. Spawn the login task
        spawn(async move {
            // Use the passed-in (and moved) auth_client directly
            let login_outcome = match auth_client.login(username.clone(), password).await
                .with_context(|| format!("Spawned login task failed for identifier: {}", username))
                {
                    Ok(response) => LoginResult::Success(response),
                    Err(e) => LoginResult::Failure(format!("{}", e)),
                };
            // Send result back to the main UI thread
            if let Err(e) = sender.send(login_outcome).await {
                error!("Failed to send login result: {}", e);
            }
        });

        // 4. Return immediately
        "Login initiated.".to_string()
    }
}

/// Handles the result received from the login task.
/// Returns true if a redraw is needed.
pub fn handle_login_result(
    result: LoginResult,
    app_state: &mut AppState,
    auth_state: &mut AuthState,
    login_state: &mut LoginState,
) -> bool {
    match result {
        LoginResult::Success(response) => {
            auth_state.auth_token = Some(response.access_token.clone());
            auth_state.user_id = Some(response.user_id.clone());
            auth_state.role = Some(response.role.clone());
            auth_state.decoded_username = Some(response.username.clone());

            // --- NEW: Save auth data to file ---
            let data_to_store = StoredAuthData {
                access_token: response.access_token.clone(),
                user_id: response.user_id.clone(),
                role: response.role.clone(),
                username: response.username.clone(),
            };

            if let Err(e) = save_auth_data(&data_to_store) {
                error!("Failed to save auth data to file: {}", e);
                // Continue anyway - user is still logged in for this session
            }
            // --- END NEW ---

            let success_message = format!(
                "Login Successful!\n\nUsername: {}\nUser ID: {}\nRole: {}",
                response.username, response.user_id, response.role
            );
            app_state.update_dialog_content(
                &success_message,
                vec!["Menu".to_string(), "Exit".to_string()],
                DialogPurpose::LoginSuccess,
            );
            info!(message = %success_message, "Login successful");
        }
        LoginResult::Failure(err_msg) | LoginResult::ConnectionError(err_msg) => {
            app_state.update_dialog_content(&err_msg, vec!["OK".to_string()], DialogPurpose::LoginFailed);
            login_state.error_message = Some(err_msg.clone());
            error!(error = %err_msg, "Login failed/connection error");
        }
    }
    login_state.username.clear();
    login_state.password.clear();
    login_state.set_has_unsaved_changes(false);
    login_state.current_cursor_pos = 0;
    true // Request redraw as dialog content changed
}
