use crate::domain::{MemoryScope, OutputFormat, TargetTool, WakeupProfile};
use clap::{Args, Parser, Subcommand, ValueEnum};
use std::path::PathBuf;

#[derive(Debug, Parser)]
#[command(
    name = "spool",
    version,
    about = "Obsidian knowledge-based context extractor"
)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Command,
}

#[derive(Debug, Subcommand)]
pub enum Command {
    Get(GetArgs),
    Explain(ExplainArgs),
    Wakeup(WakeupArgs),
    Memory(MemoryArgs),
    Mcp(McpArgs),
    Hook(HookArgs),
    Init(InitArgs),
    Status(StatusArgs),
    #[cfg(feature = "embedding")]
    Embedding(EmbeddingArgs),
    Knowledge(KnowledgeArgs),
}

#[derive(Debug, Clone, Args)]
pub struct InitArgs {
    /// Vault 根目录路径
    #[arg(long)]
    pub vault: Option<PathBuf>,
    /// 项目 ID
    #[arg(long)]
    pub project_id: Option<String>,
    /// 项目仓库路径（默认当前目录）
    #[arg(long)]
    pub repo: Option<PathBuf>,
}

#[derive(Debug, Clone, Args)]
pub struct StatusArgs {
    /// 配置文件路径（默认 ~/.spool/config.toml）
    #[arg(long)]
    pub config: Option<PathBuf>,
}

#[derive(Debug, Clone, Args)]
pub struct CommonRouteArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub task: String,
    #[arg(long)]
    pub cwd: PathBuf,
    #[arg(long, value_delimiter = ',')]
    pub files: Vec<String>,
    #[arg(long, value_enum, default_value_t = TargetValue::Claude)]
    pub target: TargetValue,
}

#[derive(Debug, Clone, Args)]
pub struct GetArgs {
    #[command(flatten)]
    pub common: CommonRouteArgs,
    #[arg(long, value_enum)]
    pub format: Option<FormatValue>,
}

#[derive(Debug, Clone, Args)]
pub struct ExplainArgs {
    #[command(flatten)]
    pub common: CommonRouteArgs,
}

#[derive(Debug, Clone, Args)]
pub struct WakeupArgs {
    #[command(flatten)]
    pub common: CommonRouteArgs,
    #[arg(long, value_enum)]
    pub profile: WakeupProfileValue,
    #[arg(long, value_enum)]
    pub format: Option<FormatValue>,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryArgs {
    #[command(subcommand)]
    pub command: MemoryCommand,
}

#[derive(Debug, Clone, Subcommand)]
pub enum MemoryCommand {
    List(MemoryListArgs),
    Show(MemoryShowArgs),
    History(MemoryShowArgs),
    RecordManual(MemoryRecordArgs),
    Propose(MemoryRecordArgs),
    Accept(MemoryActionArgs),
    Promote(MemoryActionArgs),
    Archive(MemoryActionArgs),
    Import(MemoryImportArgs),
    ImportGit(MemoryImportGitArgs),
    SyncVault(MemorySyncVaultArgs),
    Dedup(MemoryDedupArgs),
    Consolidate(MemoryConsolidateArgs),
    Prune(MemoryPruneArgs),
    Lint(MemoryLintArgs),
    SyncIndex(MemorySyncIndexArgs),
    Stats(MemoryStatsArgs),
}

#[derive(Debug, Clone, Args)]
pub struct MemoryListArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long, value_enum)]
    pub view: MemoryListViewValue,
    #[arg(long, value_enum)]
    pub format: Option<MemoryFormatValue>,
    #[arg(long)]
    pub daemon_bin: Option<PathBuf>,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryShowArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub record_id: String,
    #[arg(long, value_enum)]
    pub format: Option<MemoryFormatValue>,
    #[arg(long)]
    pub daemon_bin: Option<PathBuf>,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryActionArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub record_id: String,
    #[arg(long)]
    pub actor: Option<String>,
    #[arg(long)]
    pub reason: Option<String>,
    #[arg(long, value_delimiter = ',')]
    pub evidence_refs: Vec<String>,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryRecordArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub title: String,
    #[arg(long)]
    pub summary: String,
    #[arg(long)]
    pub memory_type: String,
    #[arg(long, value_enum)]
    pub scope: MemoryScopeValue,
    #[arg(long)]
    pub source_ref: String,
    #[arg(long)]
    pub project_id: Option<String>,
    #[arg(long)]
    pub user_id: Option<String>,
    #[arg(long)]
    pub sensitivity: Option<String>,
    #[arg(long)]
    pub actor: Option<String>,
    #[arg(long)]
    pub reason: Option<String>,
    #[arg(long, value_delimiter = ',')]
    pub evidence_refs: Vec<String>,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryImportArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 来源 provider（claude / codex），对应 session_sources 读取目录
    #[arg(long, value_enum)]
    pub provider: SessionProviderValue,
    /// provider-local session id（raw，不带 "claude:" / "codex:" 前缀）
    #[arg(long)]
    pub session_id: String,
    /// 默认只打印候选；加 --apply 才真正写入 ledger
    #[arg(long, default_value_t = false)]
    pub apply: bool,
    #[arg(long, default_value = "spool-importer")]
    pub actor: String,
    #[arg(long, value_enum, default_value_t = MemoryFormatValue::Markdown)]
    pub format: MemoryFormatValue,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryImportGitArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// Git 仓库路径（默认当前目录）
    #[arg(long)]
    pub repo: Option<PathBuf>,
    /// 扫描最近 N 条 commit（默认 30）
    #[arg(long, default_value_t = 30)]
    pub limit: usize,
    /// 只预览不写入
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
}

#[derive(Debug, Clone, Args)]
pub struct MemorySyncVaultArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 不写入文件,仅打印将会发生的动作
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    /// 对缺少 entities/tags/triggers 的记录进行启发式补充
    #[arg(long, default_value_t = false)]
    pub enrich: bool,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryDedupArgs {
    #[arg(long)]
    pub config: PathBuf,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryConsolidateArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 只预览不执行（默认行为）
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    /// 执行合并
    #[arg(long, default_value_t = false)]
    pub apply: bool,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryPruneArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 只预览不执行（默认行为）
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    /// 执行归档
    #[arg(long, default_value_t = false)]
    pub apply: bool,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryLintArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 以 JSON 输出 (默认 markdown)
    #[arg(long, default_value_t = false)]
    pub json: bool,
}

#[derive(Debug, Clone, Args)]
pub struct MemorySyncIndexArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// Write the regenerated INDEX.md (default is dry-run preview).
    #[arg(long, default_value_t = false)]
    pub apply: bool,
}

#[derive(Debug, Clone, Args)]
pub struct MemoryStatsArgs {
    #[arg(long)]
    pub config: PathBuf,
}

// ─────────────────────────────────────────────────────────────────────
// spool mcp <subcommand> — installer surface for AI client integration.
// R1 wires Claude Code only; the trait infrastructure (see
// `src/installers/`) reserves room for Codex/Cursor in P1.
// ─────────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Args)]
pub struct McpArgs {
    #[command(subcommand)]
    pub command: McpCommand,
}

#[derive(Debug, Clone, Subcommand)]
pub enum McpCommand {
    Install(McpInstallArgs),
    Update(McpUpdateArgs),
    Uninstall(McpUninstallArgs),
    Doctor(McpDoctorArgs),
}

#[derive(Debug, Clone, Args)]
pub struct McpInstallArgs {
    #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
    pub client: ClientValue,
    /// 指向 spool.toml 配置文件 (mcpServers 条目里写绝对路径)
    #[arg(long)]
    pub config: PathBuf,
    /// 自定义 spool-mcp 二进制路径 (默认走 ~/.cargo/bin/spool-mcp)
    #[arg(long)]
    pub binary_path: Option<PathBuf>,
    /// 仅打印将要发生的写入,不修改任何文件
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    /// 已存在 spool 条目时强制覆盖 (默认拒绝并要求 uninstall 后重装)
    #[arg(long, default_value_t = false)]
    pub force: bool,
    /// 输出格式
    #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
    pub format: McpReportFormat,
}

#[derive(Debug, Clone, Args)]
pub struct McpUpdateArgs {
    #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
    pub client: ClientValue,
    /// 指向 spool.toml 配置文件 (mcpServers 条目里写绝对路径)
    #[arg(long)]
    pub config: PathBuf,
    /// 自定义 spool-mcp 二进制路径 (默认走 ~/.cargo/bin/spool-mcp)
    #[arg(long)]
    pub binary_path: Option<PathBuf>,
    /// 仅打印将要发生的写入,不修改任何文件
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    /// 输出格式
    #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
    pub format: McpReportFormat,
}

#[derive(Debug, Clone, Args)]
pub struct McpUninstallArgs {
    #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
    pub client: ClientValue,
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
    pub format: McpReportFormat,
}

#[derive(Debug, Clone, Args)]
pub struct McpDoctorArgs {
    #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
    pub client: ClientValue,
    /// 指向 spool.toml 配置文件 (诊断会校验该路径存在)
    #[arg(long)]
    pub config: PathBuf,
    /// 自定义 spool-mcp 二进制路径
    #[arg(long)]
    pub binary_path: Option<PathBuf>,
    #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
    pub format: McpReportFormat,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum ClientValue {
    Claude,
    Codex,
    Cursor,
    Opencode,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum McpReportFormat {
    Text,
    Json,
}

// ─────────────────────────────────────────────────────────────────────
// spool hook <subcommand> — invoked by AI client hook scripts.
// All hooks MUST exit 0 even on internal failure (D7 in PRD); the
// `run_silent` wrapper in `src/hook_runtime/mod.rs` enforces this.
// ─────────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Args)]
pub struct HookArgs {
    #[command(subcommand)]
    pub command: HookCommand,
}

#[derive(Debug, Clone, Subcommand)]
pub enum HookCommand {
    SessionStart(HookSessionStartArgs),
    UserPrompt(HookSimpleArgs),
    PostToolUse(HookPostToolUseArgs),
    Stop(HookStopArgs),
    PreCompact(HookSimpleArgs),
}

#[derive(Debug, Clone, Args)]
pub struct HookSessionStartArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 用户当前工作目录 (默认 current_dir)
    #[arg(long)]
    pub cwd: Option<PathBuf>,
    /// 可选 task hint (默认 "session start")
    #[arg(long)]
    pub task: Option<String>,
    /// wakeup profile (默认 project)
    #[arg(long, value_enum, default_value_t = WakeupProfileValue::Project)]
    pub profile: WakeupProfileValue,
}

#[derive(Debug, Clone, Args)]
pub struct HookSimpleArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub cwd: Option<PathBuf>,
}

#[derive(Debug, Clone, Args)]
pub struct HookPostToolUseArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub cwd: Option<PathBuf>,
    /// 触发的工具名 (Bash / Edit / ...)
    #[arg(long)]
    pub tool_name: Option<String>,
    /// 工具调用 payload (会被截断到 4 KiB)
    #[arg(long)]
    pub payload: Option<String>,
}

/// Stop hook input. Supports three ways to locate the session
/// transcript (in priority order):
///   1. `--transcript-path` — explicit absolute path
///   2. `--hook-input` — raw JSON from Claude Code's stdin payload
///      containing `{"transcript_path": "..."}`
///   3. fallback: scan `~/.claude/projects/<sanitized-cwd>/` and
///      pick the most recently modified `.jsonl`
#[derive(Debug, Clone, Args)]
pub struct HookStopArgs {
    #[arg(long)]
    pub config: PathBuf,
    #[arg(long)]
    pub cwd: Option<PathBuf>,
    #[arg(long)]
    pub transcript_path: Option<PathBuf>,
    /// Raw stdin payload Claude Code sends to Stop hook. Hook script
    /// passes this in via `--hook-input "$(cat)"`.
    #[arg(long)]
    pub hook_input: Option<String>,
    /// Test/admin override for `$HOME` (used by smoke tests + dry
    /// runs). Production callers leave this empty and we read the
    /// real `$HOME`.
    #[arg(long, hide = true)]
    pub home: Option<PathBuf>,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SessionProviderValue {
    Claude,
    Codex,
}

impl SessionProviderValue {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Claude => "claude",
            Self::Codex => "codex",
        }
    }
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum TargetValue {
    Claude,
    Codex,
    Opencode,
}

impl From<TargetValue> for TargetTool {
    fn from(value: TargetValue) -> Self {
        match value {
            TargetValue::Claude => TargetTool::Claude,
            TargetValue::Codex => TargetTool::Codex,
            TargetValue::Opencode => TargetTool::Opencode,
        }
    }
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum FormatValue {
    Prompt,
    Markdown,
    Json,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum WakeupProfileValue {
    Developer,
    Project,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum MemoryListViewValue {
    PendingReview,
    WakeupReady,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum MemoryFormatValue {
    Markdown,
    Json,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum MemoryScopeValue {
    User,
    Project,
    Workspace,
    Team,
    Agent,
}

impl From<FormatValue> for OutputFormat {
    fn from(value: FormatValue) -> Self {
        match value {
            FormatValue::Prompt => OutputFormat::Prompt,
            FormatValue::Markdown => OutputFormat::Markdown,
            FormatValue::Json => OutputFormat::Json,
        }
    }
}

impl From<WakeupProfileValue> for WakeupProfile {
    fn from(value: WakeupProfileValue) -> Self {
        match value {
            WakeupProfileValue::Developer => WakeupProfile::Developer,
            WakeupProfileValue::Project => WakeupProfile::Project,
        }
    }
}

impl From<MemoryScopeValue> for MemoryScope {
    fn from(value: MemoryScopeValue) -> Self {
        match value {
            MemoryScopeValue::User => MemoryScope::User,
            MemoryScopeValue::Project => MemoryScope::Project,
            MemoryScopeValue::Workspace => MemoryScope::Workspace,
            MemoryScopeValue::Team => MemoryScope::Team,
            MemoryScopeValue::Agent => MemoryScope::Agent,
        }
    }
}

// ─────────────────────────────────────────────────────────────────────
// spool embedding <subcommand> — local embedding model management.
// Feature-gated behind `embedding`.
// ─────────────────────────────────────────────────────────────────────

#[cfg(feature = "embedding")]
#[derive(Debug, Clone, Args)]
pub struct EmbeddingArgs {
    #[command(subcommand)]
    pub command: EmbeddingCommand,
}

#[cfg(feature = "embedding")]
#[derive(Debug, Clone, Subcommand)]
pub enum EmbeddingCommand {
    /// 构建/重建嵌入索引
    Build(EmbeddingBuildArgs),
    /// 显示索引状态（记录数、模型、大小）
    Status(EmbeddingStatusArgs),
}

#[cfg(feature = "embedding")]
#[derive(Debug, Clone, Args)]
pub struct EmbeddingBuildArgs {
    #[arg(long)]
    pub config: PathBuf,
}

#[cfg(feature = "embedding")]
#[derive(Debug, Clone, Args)]
pub struct EmbeddingStatusArgs {
    #[arg(long)]
    pub config: PathBuf,
}

// ─────────────────────────────────────────────────────────────────────
// spool knowledge <subcommand> — knowledge distillation pipeline.
// ─────────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Args)]
pub struct KnowledgeArgs {
    #[command(subcommand)]
    pub command: KnowledgeCommand,
}

#[derive(Debug, Clone, Subcommand)]
pub enum KnowledgeCommand {
    /// 检测可聚合的碎片集群并生成知识页草稿
    Distill(KnowledgeDistillArgs),
}

#[derive(Debug, Clone, Args)]
pub struct KnowledgeDistillArgs {
    #[arg(long)]
    pub config: PathBuf,
    /// 只预览不写入
    #[arg(long, default_value_t = false)]
    pub dry_run: bool,
    /// 执行写入（生成 candidate 知识页）
    #[arg(long, default_value_t = false)]
    pub apply: bool,
    /// 操作者
    #[arg(long, default_value = "spool-knowledge")]
    pub actor: String,
}
