use crate::timer::RunTimer;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// A manually-created profile bound to one in-game character (matched by name
/// against the log). Each profile keeps its own guide progress, branch and timer.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(default)]
pub struct Profile {
    pub name: String,
    pub current_step: usize,
    pub league_start: bool,
    /// Show optional loot/quests/encounters (lines prefixed "optional:").
    pub show_optionals: bool,
    /// Per-profile campaign timer (with its own per-act splits).
    pub timer: RunTimer,
}

impl Default for Profile {
    fn default() -> Self {
        Profile {
            name: String::new(),
            current_step: 0,
            league_start: true,
            show_optionals: true,
            timer: RunTimer::default(),
        }
    }
}

/// Persistent user configuration. Stored as JSON in the platform config dir.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(default)]
pub struct Config {
    /// Absolute path to the game's Client.txt log file.
    pub log_path: Option<String>,
    /// Substring used to detect whether the game window is focused (matched against the active window title).
    pub poe_window_match: String,
    /// Only show overlays while the game window is focused.
    pub overlay_only_when_focused: bool,

    // --- leveling tracker overlay geometry (logical pixels) ---
    pub overlay_x: i32,
    pub overlay_y: i32,
    pub overlay_width: u32,
    pub overlay_font_size: u32,

    // --- global hotkeys (accelerator strings, e.g. "Alt+X") ---
    pub hotkey_next: String,
    pub hotkey_prev: String,
    pub hotkey_toggle: String,
    pub hotkey_timer_pause: String,
    pub hotkey_layout: String,

    // --- zone layout viewer (interactive overlay, ported from the AHK act-decoder) ---
    /// Enable the zone-layout viewer window + its hotkey.
    pub feature_layouts: bool,
    /// Saved position of the layout window (logical pixels).
    pub layout_x: i32,
    pub layout_y: i32,
    /// Saved size of the layout window (logical pixels). The image scales to fill
    /// it; the user adjusts it via the corner resize grip or the settings fields.
    pub layout_w: u32,
    pub layout_h: u32,

    // --- campaign timer ---
    pub timer_enabled: bool,
    pub timer_pause_in_town: bool,
    /// Auto-pause the timer after no mouse movement for `timer_afk_seconds`.
    pub timer_afk_enabled: bool,
    pub timer_afk_seconds: u32,
    /// Auto-pause the timer while the game window isn't focused.
    pub timer_pause_unfocused: bool,

    // --- leveling tracker state ---
    /// Live step for the active character (mirrors `progress[active_character]`).
    pub current_step: usize,
    /// Show optional loot/quests (mirrors the active profile's `show_optionals`).
    pub show_optionals: bool,
    /// Follow the league-start branch of the guide (vs. the non-league-start branch).
    pub league_start: bool,
    /// How many upcoming steps to display below the current one.
    pub lookahead: usize,
    /// Show the per-area level recommendations in the overlay.
    pub show_recommendation: bool,

    // --- character profiles & per-profile progress ---
    /// Name of the character whose profile is currently active.
    pub active_character: Option<String>,
    /// Manually-created character profiles.
    pub profiles: Vec<Profile>,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            log_path: None,
            poe_window_match: "Path of Exile".to_string(),
            overlay_only_when_focused: true,
            overlay_x: 40,
            overlay_y: 120,
            overlay_width: 460,
            overlay_font_size: 15,
            hotkey_next: "Alt+X".to_string(),
            hotkey_prev: "Alt+Z".to_string(),
            hotkey_toggle: "Alt+Shift+X".to_string(),
            hotkey_timer_pause: "Alt+P".to_string(),
            hotkey_layout: "Alt+C".to_string(),
            feature_layouts: true,
            layout_x: 600,
            layout_y: 160,
            layout_w: 480,
            layout_h: 600,
            timer_enabled: true,
            timer_pause_in_town: true,
            timer_afk_enabled: true,
            timer_afk_seconds: 60,
            timer_pause_unfocused: true,
            current_step: 0,
            show_optionals: true,
            league_start: true,
            lookahead: 2,
            show_recommendation: true,
            active_character: None,
            profiles: Vec::new(),
        }
    }
}

impl Config {
    /// Create a profile for `name` if none exists. Returns true if it was added.
    pub fn create_profile(&mut self, name: &str, league_start: bool) -> bool {
        let name = name.trim();
        if name.is_empty() || self.profiles.iter().any(|p| p.name == name) {
            return false;
        }
        self.profiles.push(Profile {
            name: name.to_string(),
            current_step: 0,
            league_start,
            show_optionals: true,
            timer: RunTimer::default(),
        });
        true
    }

    /// Remove a profile by name. If it was active, clears the active character.
    pub fn delete_profile(&mut self, name: &str) {
        self.profiles.retain(|p| p.name != name);
        if self.active_character.as_deref() == Some(name) {
            self.active_character = None;
        }
    }

    /// Make `name` the active profile, loading its step and branch into the live
    /// fields. Returns true if the active profile changed (caller may need to
    /// rebuild the guide if the league-start branch differs).
    pub fn select_profile(&mut self, name: &str) -> bool {
        if self.active_character.as_deref() == Some(name) {
            return false;
        }
        // Persist the outgoing profile's live state first.
        self.sync_progress();
        let Some(p) = self.profiles.iter().find(|p| p.name == name) else {
            return false;
        };
        self.current_step = p.current_step;
        self.league_start = p.league_start;
        self.show_optionals = p.show_optionals;
        self.active_character = Some(name.to_string());
        true
    }

    /// Mirror the live step/branch/optionals back into the active profile so they persist.
    pub fn sync_progress(&mut self) {
        let (step, league, optionals) = (self.current_step, self.league_start, self.show_optionals);
        if let Some(a) = self.active_character.clone() {
            if let Some(p) = self.profiles.iter_mut().find(|p| p.name == a) {
                p.current_step = step;
                p.league_start = league;
                p.show_optionals = optionals;
            }
        }
    }
}

fn config_dir() -> PathBuf {
    let mut dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
    dir.push("exile-ui");
    dir
}

pub fn config_path() -> PathBuf {
    let mut p = config_dir();
    p.push("config.json");
    p
}

impl Config {
    pub fn load() -> Config {
        let path = config_path();
        match std::fs::read_to_string(&path) {
            Ok(text) => serde_json::from_str(&text).unwrap_or_default(),
            Err(_) => Config::default(),
        }
    }

    pub fn save(&self) {
        let dir = config_dir();
        let _ = std::fs::create_dir_all(&dir);
        if let Ok(text) = serde_json::to_string_pretty(self) {
            let _ = std::fs::write(config_path(), text);
        }
    }
}
