use crate::config::VaultLimits;
use crate::domain::{
    CandidateNote, LifecycleCandidate, MatchedModule, MatchedProject, MatchedScene,
};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use ts_rs::TS;

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub enum TargetTool {
    Claude,
    Codex,
    Opencode,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub enum OutputFormat {
    Prompt,
    Markdown,
    Json,
}

impl OutputFormat {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Prompt => "prompt",
            Self::Markdown => "markdown",
            Self::Json => "json",
        }
    }
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct RouteInput {
    pub task: String,
    #[ts(type = "string")]
    pub cwd: PathBuf,
    pub files: Vec<String>,
    pub target: TargetTool,
    pub format: OutputFormat,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DebugTrace {
    pub matched_project_id: Option<String>,
    pub note_roots: Vec<String>,
    pub scan_roots: Vec<String>,
    pub limits: VaultLimits,
    pub note_count: usize,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct RouteResult {
    pub project: Option<MatchedProject>,
    pub modules: Vec<MatchedModule>,
    pub scenes: Vec<MatchedScene>,
    pub candidates: Vec<CandidateNote>,
    #[serde(default)]
    pub lifecycle_candidates: Vec<LifecycleCandidate>,
    pub sources: Vec<String>,
    pub debug: DebugTrace,
    /// Hint suggesting that multiple retrieved fragments share a topic and could
    /// be crystallized into a structured knowledge page.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub crystallize_hint: Option<String>,
}
