//! Backend bridge for the Tauclock application. This module binds the shared
//! state container to the Tauri command system, configures plugins, and exposes
//! convenience helpers for logging and persistence.

mod state;

use log::LevelFilter;
use serde::Serialize;
use state::{AppState, FrontendState};
use std::{path::PathBuf, str::FromStr};
use tauri_plugin_log::{Target, TargetKind};

/// Metadata and legal text displayed by the frontend "About" dialog.
#[derive(Debug, Clone, Serialize)]
struct AboutInfo {
    app_name: String,
    version: String,
    identifier: String,
    license_name: String,
    license_text: String,
}

/// Returns the current frontend snapshot without mutating any state. Used for
/// the initial UI bootstrap.
#[tauri::command]
fn get_state(app_state: tauri::State<AppState>) -> FrontendState {
    log::debug!("get_state invoked");
    app_state.snapshot()
}

/// Lightweight tick endpoint that allows the frontend to poll for changes such
/// as timer countdowns and stopwatch progress without issuing full commands.
#[tauri::command]
fn tick(app_state: tauri::State<AppState>) -> FrontendState {
    log::trace!("tick invoked");
    app_state.tick()
}

/// Creates a new alarm entry for the provided time/label combination and
/// returns the refreshed snapshot so the UI can re-render immediately.
#[tauri::command]
fn create_alarm(
    time: String,
    label: Option<String>,
    app_state: tauri::State<AppState>,
) -> FrontendState {
    let has_label = label
        .as_ref()
        .map(|value| !value.trim().is_empty())
        .unwrap_or(false);
    log::info!(
        "create_alarm invoked (time={time}, has_label={has_label})",
        time = time,
        has_label = has_label
    );
    app_state.create_alarm(time, label)
}

/// Enables or disables the specified alarm without deleting it.
#[tauri::command]
fn toggle_alarm(id: String, app_state: tauri::State<AppState>) -> FrontendState {
    log::info!("toggle_alarm invoked (id={id})", id = id);
    app_state.toggle_alarm(id)
}

/// Permanently removes the referenced alarm and emits a new snapshot.
#[tauri::command]
fn delete_alarm(id: String, app_state: tauri::State<AppState>) -> FrontendState {
    log::info!("delete_alarm invoked (id={id})", id = id);
    app_state.delete_alarm(id)
}

/// Starts, pauses, or reconfigures the countdown timer depending on the
/// requested input, optionally forcing the duration string to be reparsed.
#[tauri::command]
fn toggle_timer(
    input: Option<String>,
    force_parse: bool,
    app_state: tauri::State<AppState>,
) -> FrontendState {
    let has_input = input
        .as_ref()
        .map(|value| !value.trim().is_empty())
        .unwrap_or(false);
    log::info!(
        "toggle_timer invoked (force_parse={force_parse}, has_input={has_input})",
        force_parse = force_parse,
        has_input = has_input
    );
    app_state.toggle_timer(input, force_parse)
}

/// Resets the timer to its initial duration and stops any active countdown.
#[tauri::command]
fn reset_timer(app_state: tauri::State<AppState>) -> FrontendState {
    log::debug!("reset_timer invoked");
    app_state.reset_timer()
}

/// Starts or pauses the stopwatch while retaining any accumulated elapsed time.
#[tauri::command]
fn toggle_stopwatch(app_state: tauri::State<AppState>) -> FrontendState {
    log::info!("toggle_stopwatch invoked");
    app_state.toggle_stopwatch()
}

/// Records a lap entry at the stopwatch's current elapsed time.
#[tauri::command]
fn add_lap(app_state: tauri::State<AppState>) -> FrontendState {
    log::debug!("add_lap invoked");
    app_state.add_lap()
}

/// Clears the stopwatch, deleting all laps and resetting the elapsed counter.
#[tauri::command]
fn reset_stopwatch(app_state: tauri::State<AppState>) -> FrontendState {
    log::debug!("reset_stopwatch invoked");
    app_state.reset_stopwatch()
}

/// Returns static application metadata and the bundled MIT license text for
/// display in the About dialog.
#[tauri::command]
fn get_about_info(app: tauri::AppHandle) -> AboutInfo {
    let package = app.package_info();
    let identifier = app.config().identifier.clone();
    AboutInfo {
        app_name: package.name.clone(),
        version: package.version.to_string(),
        identifier,
        license_name: "MIT".to_string(),
        license_text: include_str!("../../LICENSE").trim().to_string(),
    }
}

/// Initializes the Tauri runtime, configures plugins, and wires shared
/// application state into every window.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let context = tauri::generate_context!();
    let storage_path = resolve_storage_path(context.config());
    let storage_display = storage_path.display().to_string();
    let app_state = AppState::new(storage_path);
    let log_level = resolve_log_level();
    let log_plugin = tauri_plugin_log::Builder::new()
        .targets([
            Target::new(TargetKind::Stdout),
            Target::new(TargetKind::Webview),
            Target::new(TargetKind::LogDir { file_name: None }),
        ])
        .level(log_level)
        .build();

    tauri::Builder::default()
        .manage(app_state)
        .plugin(log_plugin)
        .plugin(tauri_plugin_opener::init())
        .invoke_handler(tauri::generate_handler![
            get_state,
            tick,
            create_alarm,
            toggle_alarm,
            delete_alarm,
            toggle_timer,
            reset_timer,
            toggle_stopwatch,
            add_lap,
            reset_stopwatch,
            get_about_info
        ])
        .setup(move |_| {
            log::info!("Tauclock backend ready; storage_path={}", storage_display);
            log::debug!("Backend log level set to {:?}", log_level);
            Ok(())
        })
        .run(context)
        .expect("error while running tauri application");
}

/// Determines the log level from environment variables, falling back to a
/// debug build aware default.
fn resolve_log_level() -> LevelFilter {
    let env_value = std::env::var("TAUCLOCK_LOG_LEVEL")
        .or_else(|_| std::env::var("TAUCLOCK_LOG"))
        .ok();
    if let Some(raw) = env_value {
        if let Ok(level) = LevelFilter::from_str(raw.trim()) {
            return level;
        } else {
            eprintln!(
                "[tauclock] Invalid TAUCLOCK_LOG_LEVEL '{}', falling back to default",
                raw
            );
        }
    }
    if cfg!(debug_assertions) {
        LevelFilter::Debug
    } else {
        LevelFilter::Info
    }
}

/// Resolves the per-user storage file, ensuring persisted alarms live under an
/// identifier-scoped directory.
fn resolve_storage_path(config: &tauri::Config) -> PathBuf {
    let mut dir = dirs::data_dir()
        .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
    let identifier = if config.identifier.is_empty() {
        "tauclock".to_string()
    } else {
        config.identifier.clone()
    };
    dir.push(identifier);
    dir.join("alarms.json")
}
