//! ReBreak Magic für Windows — Tauri-Core.
//!
//! Alle Backend-Calls + Token-Handling laufen hier in Rust; die WebView
//! sieht nur den aggregierten UI-State (nie Tokens). Analog zur Mac-App
//! (rebreak-magic-mac), gleiche /api/magic/*-Endpoints.

mod api;
mod auth;
mod device;
mod setup;

use api::Api;
use protection_core::ProtectionState;
use serde::Serialize;
use tauri::Manager;

const DEFAULT_BACKEND_URL: &str = "https://staging.rebreak.org";

#[derive(Serialize, Clone, Default)]
#[serde(rename_all = "camelCase")]
struct UiState {
    backend_url: String,
    logged_in: bool,
    nickname: Option<String>,
    plan: Option<String>,
    device_id: String,
    hostname: String,
    /// Magic-Binding existiert (DNS-Token provisioniert)
    registered: bool,
    /// Windows-DoH zeigt aktuell auf ReBreak (lokaler Check)
    protection_applied: bool,
    release_requested_at: Option<String>,
    release_available_at: Option<String>,
}

/// Backend-URL: config.json im App-Config-Dir > Default (Staging).
/// Gleiche Semantik wie config.example.json der Mac-App.
fn backend_url(app: &tauri::AppHandle) -> String {
    if let Ok(dir) = app.path().app_config_dir() {
        if let Ok(raw) = std::fs::read_to_string(dir.join("config.json")) {
            if let Ok(v) = serde_json::from_str::<serde_json::Value>(&raw) {
                if let Some(url) = v.get("backendBaseUrl").and_then(|u| u.as_str()) {
                    return url.trim_end_matches('/').to_string();
                }
            }
        }
    }
    DEFAULT_BACKEND_URL.into()
}

/// Aggregiert den kompletten UI-State (ein Roundtrip fürs Frontend).
async fn build_state(app: &tauri::AppHandle) -> UiState {
    let mut state = UiState {
        backend_url: backend_url(app),
        device_id: device::device_id(),
        hostname: device::hostname(),
        ..Default::default()
    };

    // Lokaler DoH-Check ist auth-unabhängig
    let server_ip = protection_core::load_state()
        .map(|s| s.server_ip)
        .unwrap_or_else(setup::resolve_doh_ip);
    state.protection_applied = protection_core::doh_is_applied(&server_ip);

    let Some(token) = auth::load_session_token() else {
        return state;
    };

    let api = Api::new(&state.backend_url);
    match api.me(&token).await {
        Ok(me) => {
            state.logged_in = true;
            state.nickname = me.nickname;
            state.plan = me.plan;
        }
        Err(_) => {
            // Token abgelaufen/revoked → wie ausgeloggt behandeln
            return state;
        }
    }

    if let Ok(devices) = api.devices(&token).await {
        if let Some(mine) = devices.iter().find(|d| d.device_id == state.device_id) {
            state.registered = true;
            state.release_requested_at = mine.release_requested_at.clone();
            state.release_available_at = mine.release_available_at.clone();
        }
    }

    state
}

// ---------------------------------------------------------------------------
// Tauri-Commands — jedes gibt den frischen UiState zurück
// ---------------------------------------------------------------------------

#[tauri::command]
async fn get_state(app: tauri::AppHandle) -> Result<UiState, String> {
    Ok(build_state(&app).await)
}

/// Pairing-Code einlösen + Device registrieren (ein Schritt fürs UI).
#[tauri::command]
async fn pair_and_register(app: tauri::AppHandle, code: String) -> Result<UiState, String> {
    let code = code.trim().to_string();
    if !code.chars().all(|c| c.is_ascii_digit()) || code.len() != 6 {
        return Err("Der Code besteht aus 6 Ziffern.".into());
    }

    let base = backend_url(&app);
    let api = Api::new(&base);
    let hostname = device::hostname();

    let pair = api.redeem_pair(&code, &hostname).await?;
    auth::save_session_token(&pair.token)?;

    let reg = api
        .register(
            &pair.token,
            &device::device_id(),
            &hostname,
            "Windows-PC",
            &device::os_version(),
        )
        .await?;
    auth::save_dns_token(&reg.dns_token)?;

    Ok(build_state(&app).await)
}

/// Schutz aktivieren: EIN UAC-Prompt → DoH + State + Tamper-Service.
#[tauri::command]
async fn activate_protection(app: tauri::AppHandle) -> Result<UiState, String> {
    let dns_token = auth::load_dns_token()
        .ok_or_else(|| "Kein DNS-Token — bitte zuerst koppeln.".to_string())?;

    let server_ip = setup::resolve_doh_ip();
    let state = ProtectionState {
        doh_template: ProtectionState::doh_template_for(&dns_token),
        dns_token,
        server_ip,
        backend_url: backend_url(&app),
        device_id: device::device_id(),
    };

    let service_exe = setup::service_exe_path()?;
    let script = setup::build_setup_script(&state, &service_exe.display().to_string());
    setup::run_elevated(&script)?;

    let ui = build_state(&app).await;
    if !ui.protection_applied {
        return Err("Setup lief durch, aber der DoH-Schutz ist nicht aktiv. Bitte erneut versuchen.".into());
    }
    Ok(ui)
}

/// 24h-Cooldown starten. Teardown macht NICHT die App — der SYSTEM-Service
/// pollt /api/magic/status und baut erst ab wenn das Backend revoked.
#[tauri::command]
async fn request_release(app: tauri::AppHandle) -> Result<UiState, String> {
    let token = auth::load_session_token().ok_or("Nicht eingeloggt")?;
    let api = Api::new(&backend_url(&app));
    api.request_release(&token, &device::device_id()).await?;
    Ok(build_state(&app).await)
}

#[tauri::command]
async fn cancel_release(app: tauri::AppHandle) -> Result<UiState, String> {
    let token = auth::load_session_token().ok_or("Nicht eingeloggt")?;
    let api = Api::new(&backend_url(&app));
    api.cancel_release(&token, &device::device_id()).await?;
    Ok(build_state(&app).await)
}

/// Logout entfernt nur die Session — Schutz + Service bleiben aktiv
/// (Deaktivierung geht ausschließlich über den 24h-Release-Flow).
#[tauri::command]
async fn logout(app: tauri::AppHandle) -> Result<UiState, String> {
    auth::clear_all();
    Ok(build_state(&app).await)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            get_state,
            pair_and_register,
            activate_protection,
            request_release,
            cancel_release,
            logout,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

