// src/config.rs
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};

use crate::state::CryoState;

pub const LEGACY_PROVIDERS_DEPRECATION_WARNING: &str = "Warning: [[providers]] is deprecated; use [provider] instead. Provider rotation has been removed; only one provider is used.";

/// A named provider profile with environment variables to inject.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderConfig {
    /// Display name for logging (e.g. "anthropic", "openai")
    pub name: String,
    /// Environment variables to set when spawning the agent
    #[serde(default)]
    pub env: HashMap<String, String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CryoConfig {
    /// Agent command (e.g. "opencode", "claude", "codex")
    #[serde(default = "default_agent")]
    pub agent: String,

    /// Session timeout in seconds (0 = no timeout)
    #[serde(default)]
    pub max_session_duration: u64,

    /// Watch inbox for reactive wake
    #[serde(default = "default_watch_inbox")]
    pub watch_inbox: bool,

    /// Time of day to send periodic report (HH:MM, local time)
    #[serde(default = "default_report_time")]
    pub report_time: String,

    /// Hours between reports (0 = disabled, 24 = daily, 168 = weekly)
    #[serde(default)]
    pub report_interval: u64,

    /// Provider environment profile injected when spawning the agent.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider: Option<ProviderConfig>,

    /// Legacy provider environment profiles. `[[providers]]` is accepted for
    /// backwards compatibility but is not written by new configs.
    #[serde(default, skip_serializing)]
    pub providers: Vec<ProviderConfig>,

    /// Zulip sync polling interval in seconds (default: 5)
    #[serde(default = "default_poll_interval")]
    pub zulip_poll_interval: u64,

    /// GitHub sync polling interval in seconds (default: 5)
    #[serde(default = "default_poll_interval")]
    pub gh_poll_interval: u64,
}

fn default_agent() -> String {
    "opencode".to_string()
}

fn default_watch_inbox() -> bool {
    true
}

fn default_report_time() -> String {
    "09:00".to_string()
}

fn default_poll_interval() -> u64 {
    5
}

impl Default for CryoConfig {
    fn default() -> Self {
        Self {
            agent: default_agent(),
            max_session_duration: 0,
            watch_inbox: default_watch_inbox(),
            report_time: default_report_time(),
            report_interval: 0,
            provider: None,
            providers: Vec::new(),
            zulip_poll_interval: default_poll_interval(),
            gh_poll_interval: default_poll_interval(),
        }
    }
}

impl CryoConfig {
    /// Return the single provider profile used for all agent sessions.
    pub fn active_provider(&self) -> Option<&ProviderConfig> {
        self.provider.as_ref().or_else(|| self.providers.first())
    }

    /// True when the config used the deprecated `[[providers]]` array.
    pub fn uses_legacy_providers(&self) -> bool {
        !self.providers.is_empty()
    }

    fn normalize_legacy_provider(&mut self) {
        if self.provider.is_none() {
            self.provider = self.providers.first().cloned();
        }
    }

    /// Merge CLI overrides from timer.json into this config.
    /// Only overrides fields that were explicitly set (Some).
    pub fn apply_overrides(&mut self, state: &CryoState) {
        apply_optional_override(&mut self.agent, &state.agent_override);
        apply_optional_override(
            &mut self.max_session_duration,
            &state.max_session_duration_override,
        );
    }
}

fn apply_optional_override<T: Clone>(target: &mut T, override_value: &Option<T>) {
    if let Some(value) = override_value {
        *target = value.clone();
    }
}

pub fn config_path(dir: &Path) -> PathBuf {
    dir.join("cryo.toml")
}

pub fn load_config(path: &Path) -> Result<Option<CryoConfig>> {
    if !path.exists() {
        return Ok(None);
    }
    let contents = std::fs::read_to_string(path)?;
    let mut config: CryoConfig = toml::from_str(&contents)?;
    if config.uses_legacy_providers() {
        eprintln!("{LEGACY_PROVIDERS_DEPRECATION_WARNING}");
        config.normalize_legacy_provider();
    }
    Ok(Some(config))
}

pub fn save_config(path: &Path, config: &CryoConfig) -> Result<()> {
    let mut config = config.clone();
    config.normalize_legacy_provider();
    let toml = toml::to_string_pretty(&config)?;
    std::fs::write(path, toml)?;
    Ok(())
}

#[cfg(test)]
#[path = "unit_tests/config.rs"]
mod tests;
