mod byok_config;
mod fs_util;
mod pty;
mod settings;
mod updater;
mod vigthoria_cli;
mod workspace;

use byok_config::{byok_status, save_byok_config, ByokStatus};
use fs_util::{is_valid_project_root, list_directory, path_exists, read_workspace_file, write_workspace_file};
use pty::{PtyManager, PtySpawnOptions, PtyWritePayload};
use settings::{AppSettings, SettingsStore};
use tauri::{AppHandle, Manager, State};
use updater::WorkbenchUpdateInfo;
use vigthoria_cli::{
    detect_package_script, get_auth_status, get_auth_token, launch_login,
    login_with_credentials, run_logout, run_update, run_workspace_stabilize,
    run_workspace_stabilize_sandbox, vigthoria_http_request, VigthoriaAuthStatus,
    VigthoriaHttpResponse,
};
use workspace::{WorkspaceDefinition, WorkspaceStore};

#[derive(Default)]
pub struct AppState {
    pub pty: PtyManager,
    pub workspaces: WorkspaceStore,
    pub settings: SettingsStore,
}

#[tauri::command]
fn pty_spawn(
    app: AppHandle,
    state: State<'_, AppState>,
    options: PtySpawnOptions,
) -> Result<String, String> {
    state.pty.spawn(app, options)
}

#[tauri::command]
fn pty_write(state: State<'_, AppState>, payload: PtyWritePayload) -> Result<(), String> {
    state.pty.write(&payload.id, &payload.data)
}

#[tauri::command]
fn pty_resize(state: State<'_, AppState>, id: String, cols: u16, rows: u16) -> Result<(), String> {
    state.pty.resize(&id, cols, rows)
}

#[tauri::command]
fn pty_kill(state: State<'_, AppState>, id: String) -> Result<(), String> {
    state.pty.kill(&id)
}

#[tauri::command]
fn workspace_list(state: State<'_, AppState>) -> Result<Vec<WorkspaceDefinition>, String> {
    Ok(state.workspaces.list())
}

#[tauri::command]
fn workspace_save(
    app: AppHandle,
    state: State<'_, AppState>,
    workspace: WorkspaceDefinition,
) -> Result<WorkspaceDefinition, String> {
    let saved = state.workspaces.save(workspace);
    state.workspaces.persist(&app)?;
    Ok(saved)
}

#[tauri::command]
fn workspace_delete(app: AppHandle, state: State<'_, AppState>, id: String) -> Result<(), String> {
    state.workspaces.delete(&id)?;
    state.workspaces.persist(&app)?;
    Ok(())
}

#[tauri::command]
fn settings_get(state: State<'_, AppState>) -> Result<AppSettings, String> {
    Ok(state.settings.get())
}

#[tauri::command]
fn settings_save(
    app: AppHandle,
    state: State<'_, AppState>,
    settings: AppSettings,
) -> Result<AppSettings, String> {
    state.settings.save(&app, settings)
}

#[tauri::command]
fn detect_vigthoria_cli() -> Result<Option<String>, String> {
    Ok(which::which("vigthoria")
        .ok()
        .map(|p| p.to_string_lossy().into_owned()))
}

#[tauri::command]
async fn run_vigthoria_update(cli_path: Option<String>) -> Result<String, String> {
    // Runs on a dedicated blocking thread so a slow/stalled `vigthoria update`
    // (npm registry issues, network problems, etc.) can never freeze the
    // Workbench UI. `run_update` itself also enforces a hard timeout.
    tauri::async_runtime::spawn_blocking(move || run_update(cli_path.as_deref()))
        .await
        .map_err(|e| format!("Update task failed to complete: {e}"))?
}

#[tauri::command]
fn launch_vigthoria_login(cli_path: Option<String>) -> Result<String, String> {
    launch_login(cli_path.as_deref())
}

#[tauri::command]
async fn vigthoria_login(
    cli_path: Option<String>,
    email: String,
    password: String,
) -> Result<String, String> {
    // Runs on a dedicated blocking thread; login_with_credentials enforces
    // its own hard timeout so a stalled login process can't freeze the UI.
    tauri::async_runtime::spawn_blocking(move || {
        login_with_credentials(cli_path.as_deref(), &email, &password)
    })
    .await
    .map_err(|e| format!("Login task failed to complete: {e}"))?
}

#[tauri::command]
async fn vigthoria_logout(cli_path: Option<String>) -> Result<String, String> {
    // Runs on a dedicated blocking thread; run_logout enforces its own hard
    // timeout so a stalled CLI process can't freeze the UI.
    tauri::async_runtime::spawn_blocking(move || run_logout(cli_path.as_deref()))
        .await
        .map_err(|e| format!("Logout task failed to complete: {e}"))?
}

#[tauri::command]
fn default_shell() -> String {
    if cfg!(windows) {
        if let Ok(comspec) = std::env::var("COMSPEC") {
            let trimmed = comspec.trim();
            if !trimmed.is_empty() {
                return trimmed.to_string();
            }
        }
        r"C:\Windows\System32\cmd.exe".into()
    } else if cfg!(target_os = "macos") {
        std::env::var("SHELL").unwrap_or_else(|_| "/bin/zsh".into())
    } else {
        std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".into())
    }
}

#[tauri::command]
fn home_directory() -> Result<String, String> {
    dirs::home_dir()
        .map(|p| p.to_string_lossy().into_owned())
        .ok_or_else(|| "Could not resolve home directory".into())
}

#[tauri::command]
async fn vigthoria_auth_status(verify: Option<bool>) -> VigthoriaAuthStatus {
    let verify = verify.unwrap_or(true);
    // get_auth_status can make a blocking network call (up to ~8s); run it
    // off the main thread so it never stalls the UI message loop.
    tauri::async_runtime::spawn_blocking(move || get_auth_status(verify))
        .await
        .unwrap_or_else(|e| VigthoriaAuthStatus {
            cli_installed: false,
            has_token: false,
            authenticated: false,
            email: None,
            user_id: None,
            plan: None,
            is_admin: false,
            is_master_admin: false,
            api_url: "https://coder.vigthoria.io".into(),
            models_api_url: "https://api.vigthoria.io".into(),
            message: format!("Auth check task failed to complete: {e}"),
        })
}

#[tauri::command]
fn vigthoria_auth_token() -> Option<String> {
    get_auth_token()
}

#[tauri::command]
async fn vigthoria_http_proxy(
    method: String,
    url: String,
    token: Option<String>,
    body: Option<String>,
) -> Result<VigthoriaHttpResponse, String> {
    // Hyper-Loop/account calls can legitimately take up to ~120s; run off the
    // main thread so a long-running request never freezes the Workbench UI.
    tauri::async_runtime::spawn_blocking(move || {
        vigthoria_http_request(&method, &url, token.as_deref(), body.as_deref())
    })
    .await
    .map_err(|e| format!("HTTP proxy task failed to complete: {e}"))?
}

#[tauri::command]
fn run_workspace_stabilize_sandbox_cmd(root_path: String) -> Result<serde_json::Value, String> {
    let (success, output) = run_workspace_stabilize_sandbox(&root_path)?;
    Ok(serde_json::json!({ "success": success, "output": output }))
}

#[tauri::command]
fn run_workspace_stabilize_cmd(root_path: String) -> Result<serde_json::Value, String> {
    let (success, output) = run_workspace_stabilize(&root_path)?;
    Ok(serde_json::json!({ "success": success, "output": output }))
}

#[tauri::command]
fn package_start_command(root_path: String) -> Result<Option<String>, String> {
    detect_package_script(&root_path)
}

#[tauri::command]
fn platform() -> String {
    if cfg!(windows) {
        "windows".into()
    } else if cfg!(target_os = "macos") {
        "macos".into()
    } else {
        "linux".into()
    }
}

#[tauri::command]
fn app_version() -> String {
    updater::current_version()
}

#[tauri::command]
fn check_workbench_update() -> Result<WorkbenchUpdateInfo, String> {
    updater::check_for_update()
}

#[tauri::command]
fn download_workbench_update(url: String, sha256: String) -> Result<String, String> {
    updater::download_update(&url, &sha256)
}

#[tauri::command]
async fn get_byok_status() -> Result<ByokStatus, String> {
    // Local disk read only, but keep it off the main thread defensively so
    // the UI never stalls, consistent with the other Tauri commands here.
    tauri::async_runtime::spawn_blocking(byok_status)
        .await
        .map_err(|e| format!("BYOK status task failed to complete: {e}"))?
}

#[tauri::command]
async fn set_byok_config(
    api_key: Option<String>,
    base_url: String,
    model: String,
    clear_api_key: Option<bool>,
) -> Result<ByokStatus, String> {
    let clear_api_key = clear_api_key.unwrap_or(false);
    tauri::async_runtime::spawn_blocking(move || {
        save_byok_config(api_key, base_url, model, clear_api_key)
    })
    .await
    .map_err(|e| format!("BYOK save task failed to complete: {e}"))?
}

#[tauri::command]
fn install_workbench_update(path: String) -> Result<(), String> {
    updater::launch_installer_and_exit(&path)
}

fn diag_log(msg: &str) {
    use std::io::Write;
    let path = std::env::temp_dir().join("vigthoria-workbench-diag.log");
    if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(&path) {
        let _ = writeln!(f, "[{:?}] {}", std::time::SystemTime::now(), msg);
    }
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let default_hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |info| {
        diag_log(&format!("PANIC: {}", info));
        default_hook(info);
    }));
    diag_log("run() start");
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_store::Builder::default().build())
        .manage(AppState::default())
        .setup(|app| {
            diag_log("setup() start");
            let handle = app.handle().clone();
            let state = app.state::<AppState>();
            state.workspaces.load_from_disk(&handle);
            diag_log("workspaces loaded");
            state.settings.load_from_disk(&handle);
            diag_log("settings loaded");
            if let Some(win) = app.get_webview_window("main") {
                diag_log(&format!("main window found, visible={:?}", win.is_visible()));
                let _ = win.show();
                let _ = win.set_focus();
                diag_log(&format!("after show(), visible={:?}", win.is_visible()));
            } else {
                diag_log("main window NOT FOUND in setup()");
            }
            diag_log("setup() end");
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            pty_spawn,
            pty_write,
            pty_resize,
            pty_kill,
            workspace_list,
            workspace_save,
            workspace_delete,
            settings_get,
            settings_save,
            detect_vigthoria_cli,
            run_vigthoria_update,
            launch_vigthoria_login,
            vigthoria_login,
            vigthoria_logout,
            default_shell,
            home_directory,
            platform,
            list_directory,
            path_exists,
            is_valid_project_root,
            vigthoria_auth_status,
            vigthoria_auth_token,
            vigthoria_http_proxy,
            package_start_command,
            run_workspace_stabilize_cmd,
            run_workspace_stabilize_sandbox_cmd,
            write_workspace_file,
            read_workspace_file,
            app_version,
            check_workbench_update,
            download_workbench_update,
            install_workbench_update,
            get_byok_status,
            set_byok_config,
        ])
        .run(tauri::generate_context!())
        .expect("error while running Vigthoria Workbench");
    diag_log("run() returned (app exiting)");
}
