use std::sync::Mutex;

mod auth;

/// Tauri 2.x IPC commands for AgentKit desktop client.
///
/// Architecture: Tauri client connects to a remote AgentKit server (not a
/// local sidecar). The Rust shell acts as a thin shim that exposes the
/// remote server's connection info to the frontend, and proxies health
/// checks through the local network.
///
/// Frontend contract (see src/api/tauri.ts):
///   - start_backend()           -> u16  (remote port the frontend should use)
///   - get_backend_port()        -> u16
///   - stop_backend()            -> ()
///   - check_backend_health()    -> bool (proxied HTTP GET to remote /api/v1/health)
///   - get_backend_url()         -> String (full base URL, e.g. http://127.0.0.1:8000)
///
/// Auth commands (see `auth` module — store/load/clear refresh token in OS Keychain):
///   - store_refresh_token(token: String) -> Result<(), String>
///   - load_refresh_token()               -> Result<Option<String>, String>
///   - clear_refresh_token()              -> Result<(), String>

/// Remote server connection info. Defaults match the local dev server
/// (``agentkit serve --port 18001``). Override at compile time via
/// ``AGENTKIT_REMOTE_HOST`` / ``AGENTKIT_REMOTE_PORT``.
///
/// Note: 18001 is an intentionally uncommon port to avoid clashes with
/// common dev services (Docker's geo_backend:8000, pms-*:3306/6379/9200,
/// Vite default 5173, etc.). See vite.config.ts for the matching dev port.
const REMOTE_HOST: &str = match option_env!("AGENTKIT_REMOTE_HOST") {
    Some(h) => h,
    None => "127.0.0.1",
};
const REMOTE_PORT_STR: &str = match option_env!("AGENTKIT_REMOTE_PORT") {
    Some(p) => p,
    None => "18001",
};

/// Parse the port at first use (avoids non-const fn call in constant).
fn remote_port() -> u16 {
    REMOTE_PORT_STR.parse().unwrap_or(18001)
}

/// Global state: tracks whether the "backend" is considered started.
/// In our remote-server model this is a one-shot init flag.
#[derive(Default)]
struct BackendState {
    started: Mutex<bool>,
}

/// Start the backend connection. Returns the remote server's port.
///
/// In a local-sidecar design this would spawn the Python process and
/// return its bound port. In the remote-server model we simply record
/// that initialization completed and return the configured remote port.
#[tauri::command]
fn start_backend(state: tauri::State<BackendState>) -> Result<u16, String> {
    let mut started = state.started.lock().map_err(|e| e.to_string())?;
    *started = true;
    let port = remote_port();
    log::info!("start_backend: connecting to remote server {REMOTE_HOST}:{port}");
    Ok(port)
}

/// Get the current backend port. Errors if start_backend hasn't been
/// called yet (matches the original sidecar contract).
#[tauri::command]
fn get_backend_port(state: tauri::State<BackendState>) -> Result<u16, String> {
    let started = state.started.lock().map_err(|e| e.to_string())?;
    if !*started {
        return Err("Backend not started. Call start_backend() first.".to_string());
    }
    Ok(remote_port())
}

/// Stop the backend connection. In the remote-server model this is a
/// no-op (we don't own the server lifecycle) but we still clear the
/// started flag so a subsequent start_backend() can re-initialize.
#[tauri::command]
fn stop_backend(state: tauri::State<BackendState>) -> Result<(), String> {
    let mut started = state.started.lock().map_err(|e| e.to_string())?;
    *started = false;
    log::info!("stop_backend: cleared connection state");
    Ok(())
}

/// Probe the remote server's health endpoint. Returns true if it
/// responds with 200 OK to ``GET /api/v1/health`` within 3 seconds.
///
/// We use a synchronous HTTP client (ureq) here to keep the Tauri
/// command body simple and avoid pulling in tokio/reqwest for a single
/// health probe. The probe runs in a worker thread automatically.
#[tauri::command]
fn check_backend_health() -> Result<bool, String> {
    let url = format!("http://{REMOTE_HOST}:{}/api/v1/health", remote_port());
    log::debug!("check_backend_health: GET {url}");
    match ureq::get(&url).timeout(std::time::Duration::from_secs(3)).call() {
        Ok(resp) if resp.status() == 200 => Ok(true),
        Ok(resp) => {
            log::warn!("check_backend_health: non-200 status {}", resp.status());
            Ok(false)
        }
        Err(e) => {
            log::warn!("check_backend_health: {e}");
            Ok(false)
        }
    }
}

/// Return the full base URL the frontend should use to reach the
/// remote server. Convenience command that the frontend could use
/// instead of constructing the URL from get_backend_port().
#[tauri::command]
fn get_backend_url() -> Result<String, String> {
    Ok(format!("http://{REMOTE_HOST}:{}", remote_port()))
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .manage(BackendState::default())
        .setup(|app| {
            if cfg!(debug_assertions) {
                app.handle().plugin(
                    tauri_plugin_log::Builder::default()
                        .level(log::LevelFilter::Info)
                        .build(),
                )?;
            }
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            start_backend,
            get_backend_port,
            stop_backend,
            check_backend_health,
            get_backend_url,
            auth::store_refresh_token,
            auth::load_refresh_token,
            auth::clear_refresh_token,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
