mod sync;

use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tauri::{
    menu::{Menu, MenuItem},
    tray::TrayIconBuilder,
    AppHandle, Emitter, Manager, State,
};

use sync::api::MiniCloudApi;
use sync::engine::SyncEngine;
use sync::watcher::{FileWatcher, ChangeKind};

struct AppState {
    api: Mutex<Option<MiniCloudApi>>,
    sync_engine: Mutex<Option<SyncEngine>>,
    sync_dir: Mutex<Option<PathBuf>>,
    username: Mutex<Option<String>>,
    watcher: Mutex<Option<FileWatcher>>,
    sync_running: Arc<Mutex<bool>>,
    locked_files: Mutex<Vec<i64>>, // file IDs we have locked
}

#[tauri::command]
async fn login(
    state: State<'_, AppState>,
    server_url: String,
    username: String,
    password: String,
) -> Result<serde_json::Value, String> {
    let mut api = MiniCloudApi::new(&server_url);
    let result = api.login(&username, &password).await?;

    *state.api.lock().unwrap() = Some(api);
    *state.username.lock().unwrap() = Some(username);

    Ok(serde_json::json!({
        "username": result.user.username,
        "role": result.user.role,
    }))
}

#[tauri::command]
fn set_sync_dir(state: State<'_, AppState>, path: String) -> Result<String, String> {
    let sync_path = PathBuf::from(&path);
    if !sync_path.exists() {
        std::fs::create_dir_all(&sync_path).map_err(|e| e.to_string())?;
    }
    *state.sync_dir.lock().unwrap() = Some(sync_path);
    Ok(format!("Sync-Ordner gesetzt: {}", path))
}

#[tauri::command]
async fn start_sync(app: AppHandle, state: State<'_, AppState>) -> Result<Vec<String>, String> {
    let api = state.api.lock().unwrap().clone()
        .ok_or("Nicht eingeloggt")?;
    let sync_dir = state.sync_dir.lock().unwrap().clone()
        .ok_or("Kein Sync-Ordner gesetzt")?;

    // Full sync
    let mut engine = SyncEngine::new(sync_dir.clone(), api.clone());
    let log = engine.full_sync().await?;

    *state.sync_engine.lock().unwrap() = Some(engine);

    // Start file watcher
    let watcher = FileWatcher::new(&sync_dir)?;
    *state.watcher.lock().unwrap() = Some(watcher);

    // Start background auto-sync + watcher processing
    start_background_sync(app, state.sync_running.clone(), api, sync_dir);

    let _ = app.emit("sync-status", "synced");
    Ok(log)
}

#[tauri::command]
async fn delta_sync(state: State<'_, AppState>) -> Result<Vec<String>, String> {
    let mut engine = {
        let mut guard = state.sync_engine.lock().unwrap();
        guard.take().ok_or("Sync nicht gestartet")?
    };
    let result = engine.delta_sync().await;
    *state.sync_engine.lock().unwrap() = Some(engine);
    result
}

#[tauri::command]
async fn get_status(state: State<'_, AppState>) -> Result<serde_json::Value, String> {
    let logged_in = state.api.lock().unwrap().is_some();
    let sync_dir = state.sync_dir.lock().unwrap().clone();
    let username = state.username.lock().unwrap().clone();
    let syncing = *state.sync_running.lock().unwrap();
    let locked_count = state.locked_files.lock().unwrap().len();

    Ok(serde_json::json!({
        "logged_in": logged_in,
        "username": username,
        "sync_dir": sync_dir.map(|p| p.to_string_lossy().to_string()),
        "syncing": syncing,
        "locked_files": locked_count,
    }))
}

#[tauri::command]
async fn get_file_tree(state: State<'_, AppState>) -> Result<serde_json::Value, String> {
    let api = state.api.lock().unwrap().clone()
        .ok_or("Nicht eingeloggt")?;
    let tree = api.get_sync_tree().await?;
    Ok(serde_json::to_value(tree).map_err(|e| e.to_string())?)
}

#[tauri::command]
async fn lock_file_cmd(state: State<'_, AppState>, file_id: i64) -> Result<String, String> {
    let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?;
    api.lock_file(file_id, "Desktop Sync Client").await?;
    state.locked_files.lock().unwrap().push(file_id);
    Ok("Datei gesperrt".to_string())
}

#[tauri::command]
async fn unlock_file_cmd(state: State<'_, AppState>, file_id: i64) -> Result<String, String> {
    let api = state.api.lock().unwrap().clone().ok_or("Nicht eingeloggt")?;
    api.unlock_file(file_id).await?;
    state.locked_files.lock().unwrap().retain(|&id| id != file_id);
    Ok("Datei entsperrt".to_string())
}

/// Background sync: auto-sync every 30s + process file watcher events + heartbeat for locks
fn start_background_sync(
    app: AppHandle,
    sync_running: Arc<Mutex<bool>>,
    api: MiniCloudApi,
    sync_dir: PathBuf,
) {
    // Auto-sync thread (delta sync every 30 seconds)
    let app_sync = app.clone();
    let api_sync = api.clone();
    let sync_dir_clone = sync_dir.clone();
    let running = sync_running.clone();

    std::thread::spawn(move || {
        let rt = tokio::runtime::Runtime::new().unwrap();
        let mut engine = SyncEngine::new(sync_dir_clone, api_sync);

        loop {
            std::thread::sleep(Duration::from_secs(30));

            *running.lock().unwrap() = true;
            let _ = app_sync.emit("sync-status", "syncing");

            match rt.block_on(engine.delta_sync()) {
                Ok(log) => {
                    if !log.is_empty() {
                        let _ = app_sync.emit("sync-log", log);
                    }
                }
                Err(e) => {
                    let _ = app_sync.emit("sync-error", e);
                }
            }

            *running.lock().unwrap() = false;
            let _ = app_sync.emit("sync-status", "synced");
        }
    });

    // Heartbeat thread (every 60 seconds for locked files)
    let app_hb = app.clone();
    let api_hb = api.clone();
    std::thread::spawn(move || {
        let rt = tokio::runtime::Runtime::new().unwrap();
        loop {
            std::thread::sleep(Duration::from_secs(60));

            let state = app_hb.state::<AppState>();
            let locked = state.locked_files.lock().unwrap().clone();

            for file_id in locked {
                let _ = rt.block_on(api_hb.heartbeat(file_id));
            }
        }
    });

    // File watcher processing thread
    let app_watcher = app.clone();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep(Duration::from_secs(2));

            let state = app_watcher.state::<AppState>();
            let watcher_guard = state.watcher.lock().unwrap();
            if let Some(watcher) = watcher_guard.as_ref() {
                while let Ok(change) = watcher.receiver.try_recv() {
                    let rel_path = change.path.strip_prefix(&sync_dir)
                        .unwrap_or(&change.path)
                        .to_string_lossy()
                        .to_string();

                    let msg = match change.kind {
                        ChangeKind::Created => format!("Neu: {}", rel_path),
                        ChangeKind::Modified => format!("Geaendert: {}", rel_path),
                        ChangeKind::Deleted => format!("Geloescht: {}", rel_path),
                    };
                    let _ = app_watcher.emit("file-change", msg);
                }
            }
        }
    });
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_opener::init())
        .plugin(tauri_plugin_notification::init())
        .manage(AppState {
            api: Mutex::new(None),
            sync_engine: Mutex::new(None),
            sync_dir: Mutex::new(None),
            username: Mutex::new(None),
            watcher: Mutex::new(None),
            sync_running: Arc::new(Mutex::new(false)),
            locked_files: Mutex::new(Vec::new()),
        })
        .setup(|app| {
            // System Tray
            let quit = MenuItem::with_id(app, "quit", "Beenden", true, None::<&str>)?;
            let show = MenuItem::with_id(app, "show", "Oeffnen", true, None::<&str>)?;
            let menu = Menu::with_items(app, &[&show, &quit])?;

            TrayIconBuilder::new()
                .tooltip("Mini-Cloud Sync")
                .menu(&menu)
                .on_menu_event(|app, event| {
                    match event.id.as_ref() {
                        "quit" => std::process::exit(0),
                        "show" => {
                            if let Some(window) = app.get_webview_window("main") {
                                let _ = window.show();
                                let _ = window.set_focus();
                            }
                        }
                        _ => {}
                    }
                })
                .build(app)?;

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            login,
            set_sync_dir,
            start_sync,
            delta_sync,
            get_status,
            get_file_tree,
            lock_file_cmd,
            unlock_file_cmd,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
