use std::path::PathBuf;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MentatConfig {
    #[serde(default)]
    pub codex: Option<CodexConfig>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodexConfig {
    #[serde(default)]
    pub model: Option<String>,
    #[serde(default)]
    pub permissions: Option<CodexPermissions>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CodexPermissions {
    #[serde(default)]
    pub network_access: Option<bool>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedCodexPermissions {
    pub network_access: bool,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SessionAgentRole {
    Worker,
    Mentat,
}

impl SessionAgentRole {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Worker => "worker",
            Self::Mentat => "mentat",
        }
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SessionTranscriptEntryKind {
    UserMessage,
    AgentMessage,
    ToolEvent,
    Divider,
    SystemMessage,
}

impl SessionTranscriptEntryKind {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::UserMessage => "user_message",
            Self::AgentMessage => "agent_message",
            Self::ToolEvent => "tool_event",
            Self::Divider => "divider",
            Self::SystemMessage => "system_message",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SessionTranscriptEntry {
    pub timestamp: String,
    pub kind: SessionTranscriptEntryKind,
    #[serde(default)]
    pub agent_role: Option<SessionAgentRole>,
    pub content: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionReflectionRecord {
    pub reflection_id: String,
    pub session_id: String,
    pub created_at: String,
    pub reason: String,
    pub goal: SessionMentatTarget,
    pub content: String,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SessionImprovementStatus {
    Proposed,
    Rejected,
    Adopting,
    Adopted,
    #[serde(alias = "adopt_failed")]
    Failed,
}

impl SessionImprovementStatus {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Proposed => "proposed",
            Self::Rejected => "rejected",
            Self::Adopting => "adopting",
            Self::Adopted => "adopted",
            Self::Failed => "failed",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionImprovementRecord {
    pub improvement_id: String,
    pub reflection_id: String,
    pub session_id: String,
    pub created_at: String,
    pub goal: SessionMentatTarget,
    pub proposed_change: String,
    pub expected_benefit: String,
    pub adoption_hint: String,
    pub status: SessionImprovementStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionFile {
    pub session_id: String,
    pub cwd: PathBuf,
    pub created_at: String,
    pub updated_at: String,
    #[serde(default)]
    pub foreground_turn_id: Option<String>,
    #[serde(default)]
    pub mentat: Option<SessionMentatState>,
}

impl SessionFile {
    pub fn mentat_target(&self) -> Option<&SessionMentatTarget> {
        self.mentat
            .as_ref()
            .and_then(|mentat| mentat.target.as_ref())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SessionMentatTarget {
    pub raw_user_request: String,
    #[serde(alias = "normalized_goal")]
    pub goal: String,
    #[serde(default)]
    pub spec_path: Option<PathBuf>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SessionMentatStatus {
    Intervening,
}

impl SessionMentatStatus {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Intervening => "intervening",
        }
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SessionMentatPhase {
    SelfReflection,
    #[serde(alias = "reviewing")]
    Planning,
}

impl SessionMentatPhase {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::SelfReflection => "self_reflection",
            Self::Planning => "planning",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SessionMentatState {
    pub status: SessionMentatStatus,
    #[serde(default)]
    pub phase: Option<SessionMentatPhase>,
    #[serde(default)]
    pub target: Option<SessionMentatTarget>,
    pub source: String,
}

#[cfg(test)]
mod tests {
    use super::{
        SessionAgentRole, SessionImprovementStatus, SessionMentatPhase, SessionTranscriptEntryKind,
    };

    #[test]
    fn session_agent_role_uses_worker_wire_value() {
        assert_eq!(SessionAgentRole::Worker.as_str(), "worker");
    }

    #[test]
    fn transcript_entry_kind_uses_agent_message_wire_value() {
        assert_eq!(
            SessionTranscriptEntryKind::AgentMessage.as_str(),
            "agent_message"
        );
    }

    #[test]
    fn improvement_failed_status_uses_failed_wire_value() {
        assert_eq!(SessionImprovementStatus::Failed.as_str(), "failed");
    }

    #[test]
    fn mentat_phase_uses_planning_wire_value() {
        assert_eq!(SessionMentatPhase::Planning.as_str(), "planning");
    }

    #[test]
    fn mentat_phase_uses_self_reflection_wire_value() {
        assert_eq!(
            SessionMentatPhase::SelfReflection.as_str(),
            "self_reflection"
        );
    }
}
