mod backend;
mod config;
mod error;
mod ios_device;
mod mdm;
mod platform;
mod server;
mod sidecar;

use backend::api::{
    MagicApiClient, MagicDeviceInfo, MdmStatusByUdidData, MdmStatusData, RedeemPairingResponse,
    RegisterDeviceResponse, ReleaseResponse, UserProfile,
};
use config::{AppConfig, DesktopProtectionState, MagicSession};
use error::AppResult;
use std::process::Command;

pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_http::init())
        .plugin(tauri_plugin_os::init())
        .invoke_handler(tauri::generate_handler![
            platform::get_platform,
            server::local_http::start_local_profile_server,
            server::local_http::stop_local_profile_server,
            sidecar::supervise_magic::run_supervise_magic,
            ios_device::detect_iphone_state,
            ios_device::get_supervise_status,
            ios_device::get_installed_profiles,
            ios_device::get_installed_apps,
            ios_device::install_profile,
            ios_device::download_and_patch_enrollment_profile,
            mdm::mdm_ping,
            mdm::mdm_push,
            mdm::mdm_install_app,
            mdm::mdm_set_supervised_mode,
            mdm::mdm_take_management,
            mdm::mdm_install_lock_profile,
            redeem_pairing_code,
            register_device,
            get_stored_session,
            get_magic_devices,
            get_magic_status,
            request_release,
            cancel_release,
            start_cooldown,
            cancel_cooldown,
            logout_magic,
            download_profile,
            activate_protection,
            fetch_me,
            get_mdm_status,
            get_mdm_status_by_udid,
            link_mdm_device,
            report_device_protection_state,
            get_desktop_protection_status,
            set_desktop_protection_status,
            get_hostname,
            get_hardware_id,
            get_device_id,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

fn hostname() -> AppResult<String> {
    let output = Command::new("hostname")
        .output()
        .map_err(|e| error::AppError::new(format!("Failed to get hostname: {}", e)))?;
    let mut hostname = String::from_utf8_lossy(&output.stdout).to_string();
    hostname = hostname.trim().to_string();
    if hostname.is_empty() {
        hostname = "ReBreak Magic Device".to_string();
    }
    Ok(hostname)
}

fn get_or_init_hardware_id(config: &mut AppConfig) -> AppResult<String> {
    if let Some(id) = config.hardware_id.clone() {
        return Ok(id);
    }
    let id = platform::get_hardware_id()?;
    config.set_hardware_id(id.clone())?;
    Ok(id)
}

#[tauri::command]
fn get_hardware_id() -> AppResult<String> {
    let mut config = AppConfig::load();
    get_or_init_hardware_id(&mut config)
}

#[tauri::command]
fn get_device_id() -> AppResult<Option<String>> {
    Ok(AppConfig::load().device_id)
}

fn require_session() -> AppResult<MagicSession> {
    AppConfig::load_magic_session()?.ok_or_else(|| {
        error::AppError::new("Keine Magic-Session gefunden. Bitte neu paaren.")
    })
}

#[tauri::command]
async fn redeem_pairing_code(code: String, label: Option<String>) -> AppResult<RedeemPairingResponse> {
    let trimmed = code.trim().to_string();
    if !trimmed.chars().all(|c| c.is_ascii_digit()) || trimmed.len() != 6 {
        return Err(error::AppError::new("Der Code besteht aus 6 Ziffern.".to_string()));
    }

    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    let response = client
        .redeem_pairing_code(&trimmed, label.as_deref())
        .await?;

    let label = label.unwrap_or_else(|| hostname().unwrap_or_else(|_| "ReBreak Magic".to_string()));
    let session = MagicSession::new(response.token.clone(), response.session_id.clone(), Some(label));
    AppConfig::save_magic_session(&session)?;

    Ok(response)
}

#[tauri::command]
async fn register_device(
    model: Option<String>,
    os_version: Option<String>,
) -> AppResult<RegisterDeviceResponse> {
    let session = require_session()?;
    let mut config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    let hostname = hostname()?;

    // Hardware-ID ist der stabile Identifikator; deviceId wird vom Backend zurückgeliefert.
    let hardware_id = get_or_init_hardware_id(&mut config)?;
    let response = client
        .register_device(
            &session.access_token,
            config.device_id.as_deref(),
            Some(&hardware_id),
            &hostname,
            model.as_deref(),
            os_version.as_deref(),
        )
        .await?;

    config.set_device_id(response.device_id.clone())?;
    Ok(response)
}

#[tauri::command]
async fn get_stored_session() -> AppResult<Option<MagicSession>> {
    AppConfig::load_magic_session()
}

#[tauri::command]
async fn get_magic_devices() -> AppResult<Vec<MagicDeviceInfo>> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.list_devices(&session.access_token).await
}

#[tauri::command]
async fn get_magic_status(dns_token: String) -> AppResult<bool> {
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.get_status(&dns_token).await
}

#[tauri::command]
async fn request_release(device_id: String) -> AppResult<ReleaseResponse> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.request_release(&session.access_token, &device_id).await
}

#[tauri::command]
async fn cancel_release(device_id: String) -> AppResult<()> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.cancel_release(&session.access_token, &device_id).await
}

#[tauri::command]
async fn start_cooldown(device_id: String, duration_minutes: u32) -> AppResult<serde_json::Value> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    let result = client.start_cooldown(&session.access_token, &device_id, duration_minutes).await?;
    Ok(serde_json::json!({ "cooldownUntil": result.cooldown_until }))
}

#[tauri::command]
async fn cancel_cooldown(device_id: String) -> AppResult<()> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.cancel_cooldown(&session.access_token, &device_id).await
}

#[tauri::command]
fn logout_magic() -> AppResult<()> {
    AppConfig::clear_magic_session()
}

#[tauri::command]
fn activate_protection(profile_path: String) -> AppResult<()> {
    platform::activate_protection(&profile_path)
}

#[tauri::command]
async fn fetch_me() -> AppResult<UserProfile> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.fetch_me(&session.access_token).await
}

#[tauri::command]
async fn get_mdm_status(device_id: String) -> AppResult<MdmStatusData> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.get_mdm_status(&session.access_token, &device_id).await
}

#[tauri::command]
async fn link_mdm_device(device_id: String, mdm_id: String) -> AppResult<()> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.link_mdm_device(&session.access_token, &device_id, &mdm_id).await
}

#[tauri::command]
async fn report_device_protection_state(
    device_id: String,
    platform: String,
    protection_type: String,
    active: bool,
    reason: Option<String>,
) -> AppResult<()> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client
        .report_device_protection_state(
            &session.access_token,
            &device_id,
            &platform,
            &protection_type,
            active,
            reason.as_deref(),
        )
        .await
}

#[tauri::command]
async fn get_mdm_status_by_udid(udid: String) -> AppResult<MdmStatusByUdidData> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);
    client.get_mdm_status_by_udid(&session.access_token, &udid).await
}

#[tauri::command]
async fn download_profile(profile_url: String) -> AppResult<String> {
    let session = require_session()?;
    let config = AppConfig::load();
    let client = MagicApiClient::new(&config);

    // Try to extract dns_token from profileUrl, fallback to direct token lookup
    let dns_token = extract_dns_token(&profile_url)
        .or_else(|| Some(session.access_token.clone()))
        .ok_or_else(|| error::AppError::new("Kein DNS-Token verfügbar".to_string()))?;

    let bytes = client.download_profile(&dns_token).await?;

    let config_dir = AppConfig::config_dir()?;
    std::fs::create_dir_all(&config_dir)?;
    let profile_path = config_dir.join("rebreak-iphone-protect.mobileconfig");
    std::fs::write(&profile_path, bytes)?;

    Ok(profile_path.to_string_lossy().to_string())
}

fn extract_dns_token(profile_url: &str) -> Option<String> {
    profile_url.split("token=").nth(1).map(|s| s.split('&').next().unwrap_or(s).to_string())
}

#[tauri::command]
fn get_desktop_protection_status() -> AppResult<Option<DesktopProtectionState>> {
    AppConfig::load_desktop_protection()
}

#[tauri::command]
fn get_hostname() -> AppResult<String> {
    hostname()
}

#[tauri::command]
fn set_desktop_protection_status(active: bool, platform: String) -> AppResult<()> {
    if active {
        let state = DesktopProtectionState {
            active,
            platform,
            activated_at: chrono::Utc::now(),
        };
        AppConfig::save_desktop_protection(&state)
    } else {
        AppConfig::clear_desktop_protection()
    }
}
