#[cfg(target_os = "windows")]
use crate::server::resolve::strip_unc;
use std::path::PathBuf;
use tauri::Manager;

#[tauri::command]
pub fn get_platform_ui_scale(window: tauri::Window) -> f64 {
    window.scale_factor().unwrap_or(1.0)
}

#[tauri::command]
pub fn restart_app(app: tauri::AppHandle) {
    tauri::process::restart(&app.env());
}

#[tauri::command]
pub fn open_path(path: String) -> Result<(), String> {
    #[cfg(target_os = "windows")]
    {
        let p = strip_unc(PathBuf::from(path.trim()));
        std::process::Command::new("explorer")
            .arg(p)
            .spawn()
            .map_err(|e| e.to_string())?;
    }
    #[cfg(target_os = "macos")]
    {
        let p = std::path::Path::new(path.trim());
        std::process::Command::new("open")
            .arg(p)
            .spawn()
            .map_err(|e| e.to_string())?;
    }
    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    {
        let p = std::path::Path::new(path.trim());
        std::process::Command::new("xdg-open")
            .arg(p)
            .spawn()
            .map_err(|e| e.to_string())?;
    }
    Ok(())
}

#[tauri::command]
pub async fn pick_downloads_folder(app: tauri::AppHandle) -> Option<String> {
    use tauri_plugin_dialog::DialogExt;
    app.dialog()
        .file()
        .set_title("Choose Downloads Folder")
        .blocking_pick_folder()
        .map(|p| p.to_string())
}

#[tauri::command]
pub async fn pick_server_binary(app: tauri::AppHandle) -> Option<String> {
    use tauri_plugin_dialog::DialogExt;

    #[cfg(target_os = "windows")]
    let dialog = app
        .dialog()
        .file()
        .set_title("Choose Server Binary")
        .add_filter("Executable", &["exe", "jar", "bat", "cmd"]);

    #[cfg(target_os = "macos")]
    let dialog = app
        .dialog()
        .file()
        .set_title("Choose Server Binary")
        .add_filter("Executable or JAR", &["jar", "command", "sh", "app"]);

    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    let dialog = app
        .dialog()
        .file()
        .set_title("Choose Server Binary")
        .add_filter("Executable or JAR", &["jar", "sh"]);

    dialog.blocking_pick_file().map(|p| p.to_string())
}

#[tauri::command]
pub fn exit_app(app: tauri::AppHandle) {
    app.exit(0);
}

fn remove_dir_best_effort(path: &std::path::Path) {
    if path.is_file() {
        if let Err(e) = std::fs::remove_file(path) {
            if e.raw_os_error() == Some(32) {
                return;
            }
        }
    } else if path.is_dir() {
        if let Ok(entries) = std::fs::read_dir(path) {
            for entry in entries.flatten() {
                remove_dir_best_effort(&entry.path());
            }
        }
        let _ = std::fs::remove_dir(path);
    }
}

fn wait_until_deletable(path: &std::path::Path, timeout_secs: u64) -> bool {
    let deadline = std::time::Instant::now() + std::time::Duration::from_secs(timeout_secs);
    while std::time::Instant::now() < deadline {
        let locked = if path.is_file() {
            std::fs::OpenOptions::new().write(true).open(path).is_err()
        } else if path.is_dir() {
            std::fs::read_dir(path).is_err()
        } else {
            return true;
        };
        if !locked {
            return true;
        }
        std::thread::sleep(std::time::Duration::from_millis(200));
    }
    false
}

#[tauri::command]
pub async fn clear_moku_cache(app: tauri::AppHandle) -> Result<(), String> {
    let window = app.get_webview_window("main").ok_or("no main window")?;

    let (tx, rx) = tokio::sync::oneshot::channel::<Result<(), String>>();

    window
        .with_webview(move |_wv| {
            let _ = tx.send(Ok(()));
        })
        .map_err(|e| e.to_string())?;

    rx.await.map_err(|e| e.to_string())??;

    let cache_dir = app.path().app_cache_dir().map_err(|e| e.to_string())?;
    if cache_dir.exists() {
        wait_until_deletable(&cache_dir, 3);
        remove_dir_best_effort(&cache_dir);
        std::fs::create_dir_all(&cache_dir).map_err(|e| e.to_string())?;
    }

    Ok(())
}

#[tauri::command]
pub fn clear_suwayomi_cache() -> Result<(), String> {
    use crate::server::resolve::suwayomi_data_dir;
    let data_dir = suwayomi_data_dir();
    for dir in &["cache/kcef", "logs"] {
        let p = data_dir.join(dir);
        if p.exists() {
            remove_dir_best_effort(&p);
        }
    }
    for dir in &["downloads/thumbnails"] {
        let p = data_dir.join(dir);
        if p.exists() {
            remove_dir_best_effort(&p);
            let _ = std::fs::create_dir_all(&p);
        }
    }
    Ok(())
}

#[tauri::command]
pub fn reset_suwayomi_data(app: tauri::AppHandle) -> Result<(), String> {
    use crate::server::resolve::suwayomi_data_dir;

    crate::server::kill_tachidesk(&app);

    let data_dir = suwayomi_data_dir();
    let targets = ["database.mv.db", "extensions", "settings", "logs", "local"];

    for entry_name in &targets {
        let p = data_dir.join(entry_name);
        if p.exists() {
            wait_until_deletable(&p, 10);
        }
    }

    for entry_name in &targets {
        let p = data_dir.join(entry_name);
        if p.is_dir() {
            std::fs::remove_dir_all(&p).map_err(|e| format!("{entry_name}: {e}"))?;
        } else if p.exists() {
            std::fs::remove_file(&p).map_err(|e| format!("{entry_name}: {e}"))?;
        }
    }
    Ok(())
}