use crate::commands::persona::Persona;

#[derive(Debug, Clone)]
pub struct Config {
    pub enabled: bool,
    pub show_header: bool,
    pub max_lines: usize,
    pub dedup_min: usize,
    pub git_log_max_commits: usize,
    pub git_diff_max_lines: usize,
    pub docker_logs_max_lines: usize,
    pub find_max_results: usize,
    pub bypass: Vec<String>,
    pub compact_threshold_tokens: u64,
    pub memory_retention_days: u32,
    // ── Context engine flags ────────────────────────────────────────────
    pub adaptive_intensity: bool,
    pub context_cache_enabled: bool,
    pub redundancy_cache_enabled: bool,
    pub summarize_threshold_lines: usize,
    // ── Output / memory-file flags ──────────────────────────────────────
    pub persona: Persona,
    pub auto_compress_md: bool,
    pub lang: String,
    // ── Token economy (phase 7) ───────────────────────────────────────────
    /// Fraction of budget at which sub-agent cost triggers a warning (default 0.50).
    pub agent_warn_threshold_pct: f32,
    /// Predict pressure warning when calls remaining < this (default 20).
    pub burn_rate_warn_calls: u64,
    /// Estimated tokens per sub-agent spawn (default 200_000).
    pub agent_spawn_cost: u64,
    /// Max lines injected into Read tool_input (0 = disabled, default 0).
    pub read_max_lines: usize,
    /// Max results injected into Grep tool_input (0 = disabled, default 0).
    pub grep_max_results: usize,
    // ── Tunables (phase 5) ──────────────────────────────────────────────
    /// Max entries in the rolling call log (default 32).
    pub max_call_log: usize,
    /// How many recent calls are eligible for redundancy lookup (default 16).
    pub recent_window: u64,
    /// Minimum Jaccard similarity for fuzzy redundancy match (default 0.85).
    pub similarity_threshold: f32,
    /// Fraction of context budget at which Full graduates to Ultra (default 0.80).
    pub ultra_trigger_pct: f32,
    /// Default `n` for `squeez_prior_summaries` MCP tool (default 5).
    pub mcp_prior_summaries_default: usize,
    /// Default `n` for `squeez_recent_calls` MCP tool (default 10).
    pub mcp_recent_calls_default: usize,
    /// Calls remaining threshold for State-First Pattern warning (default 10).
    pub state_warn_calls: u64,
    // ── Signature-mode (US-001) ─────────────────────────────────────────────
    /// Enable signature-mode compression for large code files (default true).
    pub sig_mode_enabled: bool,
    /// Minimum line count to trigger signature-mode (default 400).
    pub sig_mode_threshold_lines: usize,
    /// Also capture indented method signatures inside impl/class blocks (default false).
    pub sig_mode_include_indented: bool,
    // ── Memory-file size warning (US-002) ───────────────────────────────────
    /// Token threshold above which inject_memory emits a size warning banner
    /// inside the squeez block. Set to 0 to disable. Default: 1000.
    pub memory_file_warn_tokens: usize,
    // ── Structured summary format (US-003) ──────────────────────────────────
    /// Summary output shape: "prose" | "structured" | "auto".
    /// "auto" selects Structured when Intensity == Ultra, Prose otherwise.
    pub summary_format: String,
    // ── Agent prompt compression ─────────────────────────────────────────────
    /// Max token estimate for Agent/Task prompt before compression kicks in.
    /// 0 = disabled. Default: 2000.
    pub agent_prompt_max_tokens: usize,
    /// Pass through all output uncompressed when SQUEEZ_PLAN_MODE=1 env var is set.
    /// Default: true. Set to false to compress even in plan mode.
    pub plan_mode_passthrough: bool,
    // ── Auto-curation nudges (item 1) ────────────────────────────────────────
    /// Emit `[squeez: hint ...]` markers when recurring patterns are detected.
    /// Default: true.
    pub nudge_enabled: bool,
    /// Recurrence count at which a repeated error fingerprint triggers a nudge.
    /// Default: 3.
    pub nudge_error_threshold: u32,
    /// Modification count at which a repeatedly-edited file triggers a nudge.
    /// Default: 5.
    pub nudge_file_mod_threshold: u32,
    /// Repeat count at which an expensive command triggers a nudge.
    /// Default: 4.
    pub nudge_cmd_repeat_threshold: u32,
    // ── Continuous handler calibration (item 2) ──────────────────────────────
    /// Accumulate per-handler in/out token counts in handler_stats.json so
    /// `squeez_handler_stats` can surface under/over-performers. Default: true.
    pub handler_stats_enabled: bool,
    // ── PostToolUse compression for Read/Edit/Write (gap fix) ────────────────
    /// Override `summarize_threshold_lines` when the tool is Read. Default 150.
    /// Many code files are 80-300 lines and never triggered the global default
    /// of 300, leaving them uncompressed. Set to 0 to fall back to the global
    /// `summarize_threshold_lines`.
    pub read_summarize_threshold_lines: usize,
    /// Strip the verbose `"Here's the result of running cat -n on a snippet of
    /// the edited file:"` preamble from Edit/Write tool results. Default: true.
    pub strip_edit_preamble: bool,
    // ── Real-context tracking (transcript audit CF-1/CF-2) ──────────────────
    /// Host context window in tokens. When > 0 this replaces the
    /// `compact_threshold_tokens * 5/4` budget formula so adaptive intensity
    /// and burn-rate math are keyed to the real window (e.g. 200000, or
    /// 1000000 for a `[1m]` model). 0 = keep the legacy formula. Default: 0.
    pub context_window_tokens: u64,
    /// Token threshold above which a sub-agent's returned result triggers a
    /// warning suggesting the file + short-summary return pattern. A 24K-token
    /// plan riding in the prefix for 100 turns costs ~2.4M cache-read tokens.
    /// 0 = disabled. Default: 3000.
    pub subagent_result_warn_tokens: usize,
    /// Occurrence count at which a repeated quota/plan-limit error (e.g.
    /// "rate limit", "upgrade your plan") triggers a stop-retrying warning.
    /// Lower than `nudge_error_threshold` because quota errors are never
    /// transient. 0 = disabled. Default: 2.
    pub quota_error_threshold: u32,
    // ── Workflow abuse prevention (audit findings) ───────────────────────────
    /// Seconds of inactivity before warning that the 5-min ephemeral cache may
    /// have expired. Warn at 4.5 min so the user can act before the rebuild hits.
    /// 0 = disabled. Default: 270.
    pub cache_idle_warn_secs: u64,
    /// Time window (seconds) for detecting parallel-agent bursts. Default: 120.
    pub parallel_agent_burst_window_secs: u64,
    /// Number of agents spawned within `parallel_agent_burst_window_secs` that
    /// triggers a budget warning. Default: 5.
    pub parallel_agent_burst_threshold: usize,
    /// Cumulative subagent output tokens at which a total-cost warning fires
    /// for the session. 0 = disabled. Default: 50_000.
    pub subagent_total_warn_tokens: u64,
    /// Tool calls per 60-second window that triggers a high-call-rate warning.
    /// Detects runaway agent loops. 0 = disabled. Default: 20.
    pub call_rate_warn_per_min: u32,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            enabled: true,
            show_header: true,
            max_lines: 120,
            dedup_min: 2,
            git_log_max_commits: 20,
            git_diff_max_lines: 150,
            docker_logs_max_lines: 100,
            find_max_results: 50,
            bypass: vec![
                "docker exec".to_string(),
                "psql".to_string(),
                "mysql".to_string(),
                "ssh".to_string(),
            ],
            compact_threshold_tokens: 90_000,
            memory_retention_days: 30,
            adaptive_intensity: true,
            context_cache_enabled: true,
            redundancy_cache_enabled: true,
            summarize_threshold_lines: 300,
            persona: Persona::Ultra,
            auto_compress_md: true,
            lang: "en".to_string(),
            agent_warn_threshold_pct: 0.50,
            burn_rate_warn_calls: 30,
            agent_spawn_cost: 350_000,
            read_max_lines: 300,
            grep_max_results: 100,
            max_call_log: 32,
            recent_window: 16,
            similarity_threshold: 0.85,
            ultra_trigger_pct: 0.65,
            mcp_prior_summaries_default: 5,
            mcp_recent_calls_default: 10,
            state_warn_calls: 10,
            sig_mode_enabled: true,
            sig_mode_threshold_lines: 400,
            sig_mode_include_indented: false,
            memory_file_warn_tokens: 1000,
            summary_format: "auto".to_string(),
            agent_prompt_max_tokens: 2000,
            plan_mode_passthrough: true,
            nudge_enabled: true,
            nudge_error_threshold: 3,
            nudge_file_mod_threshold: 5,
            nudge_cmd_repeat_threshold: 4,
            handler_stats_enabled: true,
            read_summarize_threshold_lines: 150,
            strip_edit_preamble: true,
            context_window_tokens: 0,
            subagent_result_warn_tokens: 3000,
            quota_error_threshold: 2,
            cache_idle_warn_secs: 270,
            parallel_agent_burst_window_secs: 120,
            parallel_agent_burst_threshold: 5,
            subagent_total_warn_tokens: 50_000,
            call_rate_warn_per_min: 20,
        }
    }
}

impl Config {
    pub fn from_str(s: &str) -> Self {
        let mut c = Self::default();
        for line in s.lines() {
            let line = line.trim();
            if line.is_empty() || line.starts_with('#') {
                continue;
            }
            if let Some((k, v)) = line.split_once('=') {
                let (k, v) = (k.trim(), v.trim());
                match k {
                    "enabled" => c.enabled = v == "true",
                    "show_header" => c.show_header = v == "true",
                    "max_lines" => c.max_lines = v.parse().unwrap_or(c.max_lines),
                    "dedup_min" => c.dedup_min = v.parse().unwrap_or(c.dedup_min),
                    "git_log_max_commits" => {
                        c.git_log_max_commits = v.parse().unwrap_or(c.git_log_max_commits)
                    }
                    "git_diff_max_lines" => {
                        c.git_diff_max_lines = v.parse().unwrap_or(c.git_diff_max_lines)
                    }
                    "docker_logs_max_lines" => {
                        c.docker_logs_max_lines = v.parse().unwrap_or(c.docker_logs_max_lines)
                    }
                    "find_max_results" => {
                        c.find_max_results = v.parse().unwrap_or(c.find_max_results)
                    }
                    "bypass" => c.bypass = v.split(',').map(|s| s.trim().to_string()).collect(),
                    "compact_threshold_tokens" => {
                        c.compact_threshold_tokens = v.parse().unwrap_or(c.compact_threshold_tokens)
                    }
                    "memory_retention_days" => {
                        c.memory_retention_days = v.parse().unwrap_or(c.memory_retention_days)
                    }
                    "adaptive_intensity" => c.adaptive_intensity = v == "true",
                    "context_cache_enabled" => c.context_cache_enabled = v == "true",
                    "redundancy_cache_enabled" => c.redundancy_cache_enabled = v == "true",
                    "summarize_threshold_lines" => {
                        c.summarize_threshold_lines =
                            v.parse().unwrap_or(c.summarize_threshold_lines)
                    }
                    "persona" => c.persona = crate::commands::persona::from_str(v),
                    "auto_compress_md" => c.auto_compress_md = v == "true",
                    "lang" => c.lang = v.to_string(),
                    "agent_warn_threshold_pct" => {
                        c.agent_warn_threshold_pct =
                            v.parse().unwrap_or(c.agent_warn_threshold_pct)
                    }
                    "burn_rate_warn_calls" => {
                        c.burn_rate_warn_calls =
                            v.parse().unwrap_or(c.burn_rate_warn_calls)
                    }
                    "agent_spawn_cost" => {
                        c.agent_spawn_cost = v.parse().unwrap_or(c.agent_spawn_cost)
                    }
                    "read_max_lines" => {
                        c.read_max_lines = v.parse().unwrap_or(c.read_max_lines)
                    }
                    "grep_max_results" => {
                        c.grep_max_results = v.parse().unwrap_or(c.grep_max_results)
                    }
                    "max_call_log" => {
                        c.max_call_log = v.parse().unwrap_or(c.max_call_log)
                    }
                    "recent_window" => {
                        c.recent_window = v.parse().unwrap_or(c.recent_window)
                    }
                    "similarity_threshold" => {
                        c.similarity_threshold = v.parse().unwrap_or(c.similarity_threshold)
                    }
                    "ultra_trigger_pct" => {
                        c.ultra_trigger_pct = v.parse().unwrap_or(c.ultra_trigger_pct)
                    }
                    "mcp_prior_summaries_default" => {
                        c.mcp_prior_summaries_default =
                            v.parse().unwrap_or(c.mcp_prior_summaries_default)
                    }
                    "mcp_recent_calls_default" => {
                        c.mcp_recent_calls_default =
                            v.parse().unwrap_or(c.mcp_recent_calls_default)
                    }
                    "state_warn_calls" => {
                        c.state_warn_calls = v.parse().unwrap_or(c.state_warn_calls)
                    }
                    "sig_mode_enabled" => c.sig_mode_enabled = v == "true",
                    "sig_mode_threshold_lines" => {
                        c.sig_mode_threshold_lines =
                            v.parse().unwrap_or(c.sig_mode_threshold_lines)
                    }
                    "sig_mode_include_indented" => {
                        c.sig_mode_include_indented = v == "true"
                    }
                    "memory_file_warn_tokens" => {
                        c.memory_file_warn_tokens =
                            v.parse().unwrap_or(c.memory_file_warn_tokens)
                    }
                    "summary_format" => {
                        c.summary_format = match v {
                            "prose" | "structured" | "auto" => v.to_string(),
                            _ => "auto".to_string(),
                        };
                    }
                    "agent_prompt_max_tokens" => {
                        c.agent_prompt_max_tokens =
                            v.parse().unwrap_or(c.agent_prompt_max_tokens)
                    }
                    "plan_mode_passthrough" => c.plan_mode_passthrough = v == "true",
                    "nudge_enabled" => c.nudge_enabled = v == "true",
                    "nudge_error_threshold" => {
                        c.nudge_error_threshold =
                            v.parse().unwrap_or(c.nudge_error_threshold)
                    }
                    "nudge_file_mod_threshold" => {
                        c.nudge_file_mod_threshold =
                            v.parse().unwrap_or(c.nudge_file_mod_threshold)
                    }
                    "nudge_cmd_repeat_threshold" => {
                        c.nudge_cmd_repeat_threshold =
                            v.parse().unwrap_or(c.nudge_cmd_repeat_threshold)
                    }
                    "read_summarize_threshold_lines" => {
                        c.read_summarize_threshold_lines =
                            v.parse().unwrap_or(c.read_summarize_threshold_lines)
                    }
                    "strip_edit_preamble" => c.strip_edit_preamble = v == "true",
                    "handler_stats_enabled" => c.handler_stats_enabled = v == "true",
                    "context_window_tokens" => {
                        c.context_window_tokens = v.parse().unwrap_or(c.context_window_tokens)
                    }
                    "subagent_result_warn_tokens" => {
                        c.subagent_result_warn_tokens =
                            v.parse().unwrap_or(c.subagent_result_warn_tokens)
                    }
                    "quota_error_threshold" => {
                        c.quota_error_threshold =
                            v.parse().unwrap_or(c.quota_error_threshold)
                    }
                    "cache_idle_warn_secs" => {
                        c.cache_idle_warn_secs =
                            v.parse().unwrap_or(c.cache_idle_warn_secs)
                    }
                    "parallel_agent_burst_window_secs" => {
                        c.parallel_agent_burst_window_secs =
                            v.parse().unwrap_or(c.parallel_agent_burst_window_secs)
                    }
                    "parallel_agent_burst_threshold" => {
                        c.parallel_agent_burst_threshold =
                            v.parse().unwrap_or(c.parallel_agent_burst_threshold)
                    }
                    "subagent_total_warn_tokens" => {
                        c.subagent_total_warn_tokens =
                            v.parse().unwrap_or(c.subagent_total_warn_tokens)
                    }
                    "call_rate_warn_per_min" => {
                        c.call_rate_warn_per_min =
                            v.parse().unwrap_or(c.call_rate_warn_per_min)
                    }
                    _ => {}
                }
            }
        }
        c
    }

    pub fn load() -> Self {
        let base = std::env::var("SQUEEZ_DIR").unwrap_or_else(|_| {
            format!("{}/.claude/squeez", crate::session::home_dir())
        });
        let path = format!("{}/config.ini", base);
        std::fs::read_to_string(&path)
            .map(|s| Self::from_str(&s))
            .unwrap_or_default()
    }

    pub fn is_bypassed(&self, cmd: &str) -> bool {
        self.bypass.iter().any(|b| cmd.starts_with(b.as_str()))
    }
}
