//! 桌面 facade 的请求/响应 DTO，以及 DTO 的轻量映射 impl。

use super::helpers::parse_csv_items;
use crate::domain::{
    ContextBundle, MemoryScope, OutputFormat, RouteInput, TargetTool, WakeupPacket, WakeupProfile,
};
use crate::lifecycle_service::{LifecycleAction, LifecycleWorkbenchSnapshot};
use crate::lifecycle_store::{
    LedgerEntry, ProposeMemoryRequest, RecordMemoryRequest, TransitionMetadata,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::path::PathBuf;
use ts_rs::TS;

// ---- Requests ----

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

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWakeupRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    #[ts(type = "string | null")]
    pub vault_root_override: Option<PathBuf>,
    #[ts(type = "string")]
    pub cwd: PathBuf,
    pub task: String,
    pub files: Vec<String>,
    pub target: TargetTool,
    pub profile: WakeupProfile,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopPromptOptimizeRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    #[ts(type = "string | null")]
    pub vault_root_override: Option<PathBuf>,
    #[ts(type = "string")]
    pub cwd: PathBuf,
    pub task: String,
    pub files: Vec<String>,
    pub target: TargetTool,
    pub profile: WakeupProfile,
    pub provider: Option<String>,
    pub session_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWorkbenchRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub daemon: Option<DesktopDaemonRequest>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopRecordLookupRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub record_id: String,
    pub daemon: Option<DesktopDaemonRequest>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionBrowserRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub page: usize,
    pub per_page: usize,
    pub provider: Option<String>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionDetailRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub session_id: String,
    pub message_offset: Option<usize>,
    pub message_limit: Option<usize>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionActionRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub session_id: String,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopStatusRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    #[ts(type = "string | null")]
    pub vault_root_override: Option<PathBuf>,
    #[ts(type = "string")]
    pub cwd: PathBuf,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWikiLintRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWikiIndexRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    #[serde(default)]
    pub project_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopImportSessionRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub provider: String,
    pub session_id: String,
    #[serde(default)]
    pub apply: bool,
    #[serde(default)]
    pub actor: Option<String>,
}

pub type DesktopImportSessionResponse = crate::memory_importer::ImportSessionResponse;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopMemoryDraftRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub title: String,
    pub summary: String,
    pub memory_type: String,
    pub scope: MemoryScope,
    pub source_ref: String,
    pub project_id: Option<String>,
    pub user_id: Option<String>,
    pub sensitivity: Option<String>,
    pub metadata: DesktopMetadataDto,
    // Structured retrieval signals
    #[serde(default)]
    pub entities: Vec<String>,
    #[serde(default)]
    pub tags: Vec<String>,
    #[serde(default)]
    pub triggers: Vec<String>,
    #[serde(default)]
    pub related_files: Vec<String>,
    #[serde(default)]
    pub related_records: Vec<String>,
    #[serde(default)]
    pub supersedes: Option<String>,
    #[serde(default)]
    pub applies_to: Vec<String>,
    #[serde(default)]
    pub valid_until: Option<String>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopMemoryActionRequest {
    #[ts(type = "string")]
    pub config_path: PathBuf,
    pub record_id: String,
    pub action: DesktopLifecycleActionDto,
    pub metadata: DesktopMetadataDto,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopDaemonRequest {
    pub enabled: bool,
    #[ts(type = "string | null")]
    pub daemon_bin: Option<PathBuf>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopMetadataDto {
    pub actor: Option<String>,
    pub reason: Option<String>,
    pub evidence_refs: Vec<String>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, ts_rs::TS)]
#[serde(rename_all = "snake_case")]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub enum DesktopLifecycleActionDto {
    Accept,
    Promote,
    Archive,
}

// ---- Responses ----

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopContextResponse {
    pub rendered: String,
    pub explain: String,
    pub used_format: OutputFormat,
    #[ts(type = "string")]
    pub used_vault_root: PathBuf,
    pub bundle: ContextBundle,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWakeupResponse {
    #[ts(type = "string")]
    pub used_vault_root: PathBuf,
    pub bundle: ContextBundle,
    pub packet: WakeupPacket,
}

pub type DesktopPromptOptimizeResponse = crate::memory_gateway::PromptOptimizeResponse;

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWorkbenchResponse {
    pub snapshot: LifecycleWorkbenchSnapshot,
    #[ts(type = "unknown")]
    pub payload: Value,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopRecordResponse {
    pub record: LedgerEntry,
    #[ts(type = "unknown")]
    pub payload: Value,
    pub rendered: String,
    pub available_actions: Vec<DesktopLifecycleActionDto>,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopHistoryResponse {
    pub record_id: String,
    pub history: Vec<LedgerEntry>,
    #[ts(type = "unknown")]
    pub payload: Value,
    pub rendered: String,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWriteResponse {
    pub entry: LedgerEntry,
    #[ts(type = "unknown")]
    pub payload: Value,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionItem {
    pub provider: String,
    pub session_id: String,
    pub title: String,
    pub summary: Option<String>,
    pub prompt_preview: Option<String>,
    pub cwd: Option<String>,
    pub source_path: Option<String>,
    pub project_path: Option<String>,
    pub updated_at: String,
    pub record_count: usize,
    pub pending_review_count: usize,
    pub wakeup_ready_count: usize,
    pub titles: Vec<String>,
    pub memory_types: Vec<String>,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionBrowserResponse {
    pub sessions: Vec<DesktopSessionItem>,
    pub page: usize,
    pub per_page: usize,
    pub total: usize,
    pub has_more: bool,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionMessage {
    pub role: String,
    pub timestamp: String,
    pub content: String,
    pub truncated: bool,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionDetailResponse {
    pub session: DesktopSessionItem,
    pub records: Vec<LedgerEntry>,
    pub messages: Vec<DesktopSessionMessage>,
    pub total_messages: usize,
    pub showing_recent_messages: usize,
    pub has_more_messages: bool,
    pub latest_user_message: Option<String>,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopSessionActionResponse {
    pub session_id: String,
    pub provider: String,
    pub command: Option<String>,
    pub message: String,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWikiLintResponse {
    #[ts(type = "string")]
    pub used_vault_root: PathBuf,
    pub report: crate::wiki_lint::LintReport,
    pub markdown: String,
}

#[derive(Debug, Clone, Serialize, TS)]
#[ts(export, export_to = "../frontend/src/lib/types/generated/")]
pub struct DesktopWikiIndexResponse {
    #[ts(type = "string")]
    pub used_vault_root: PathBuf,
    pub markdown: Option<String>,
}

// ---- DTO 映射 impl ----

impl DesktopRouteRequest {
    pub(super) fn to_route_input(&self) -> RouteInput {
        RouteInput {
            task: self.task.clone(),
            cwd: self.cwd.clone(),
            files: self.files.clone(),
            target: self.target,
            format: self.format,
        }
    }
}

impl DesktopWakeupRequest {
    pub(super) fn to_route_input(&self) -> RouteInput {
        RouteInput {
            task: self.task.clone(),
            cwd: self.cwd.clone(),
            files: self.files.clone(),
            target: self.target,
            format: OutputFormat::Json,
        }
    }
}

impl DesktopPromptOptimizeRequest {
    pub(super) fn to_route_input(&self) -> RouteInput {
        RouteInput {
            task: self.task.clone(),
            cwd: self.cwd.clone(),
            files: self.files.clone(),
            target: self.target,
            format: OutputFormat::Prompt,
        }
    }
}

impl DesktopMemoryDraftRequest {
    pub(super) fn to_record_request(&self) -> RecordMemoryRequest {
        RecordMemoryRequest {
            title: self.title.clone(),
            summary: self.summary.clone(),
            memory_type: self.memory_type.clone(),
            scope: self.scope,
            source_ref: self.source_ref.clone(),
            project_id: self.project_id.clone(),
            user_id: self.user_id.clone(),
            sensitivity: self.sensitivity.clone(),
            metadata: self.metadata.clone().into_transition_metadata(),
            entities: self.entities.clone(),
            tags: self.tags.clone(),
            triggers: self.triggers.clone(),
            related_files: self.related_files.clone(),
            related_records: self.related_records.clone(),
            supersedes: self.supersedes.clone(),
            applies_to: self.applies_to.clone(),
            valid_until: self.valid_until.clone(),
        }
    }

    pub(super) fn to_propose_request(&self) -> ProposeMemoryRequest {
        ProposeMemoryRequest {
            title: self.title.clone(),
            summary: self.summary.clone(),
            memory_type: self.memory_type.clone(),
            scope: self.scope,
            source_ref: self.source_ref.clone(),
            project_id: self.project_id.clone(),
            user_id: self.user_id.clone(),
            sensitivity: self.sensitivity.clone(),
            metadata: self.metadata.clone().into_transition_metadata(),
            entities: self.entities.clone(),
            tags: self.tags.clone(),
            triggers: self.triggers.clone(),
            related_files: self.related_files.clone(),
            related_records: self.related_records.clone(),
            supersedes: self.supersedes.clone(),
            applies_to: self.applies_to.clone(),
            valid_until: self.valid_until.clone(),
        }
    }
}

impl DesktopMetadataDto {
    pub fn from_evidence_csv(value: &str) -> Self {
        Self {
            actor: None,
            reason: None,
            evidence_refs: parse_csv_items(value),
        }
    }

    pub(super) fn into_transition_metadata(self) -> TransitionMetadata {
        TransitionMetadata {
            actor: self.actor,
            reason: self.reason,
            evidence_refs: self.evidence_refs,
        }
    }
}

impl DesktopLifecycleActionDto {
    pub(super) fn from_lifecycle_action(action: LifecycleAction) -> Self {
        match action {
            LifecycleAction::Accept => Self::Accept,
            LifecycleAction::PromoteToCanonical => Self::Promote,
            LifecycleAction::Archive => Self::Archive,
        }
    }

    pub(super) fn into_lifecycle_action(self) -> LifecycleAction {
        match self {
            Self::Accept => LifecycleAction::Accept,
            Self::Promote => LifecycleAction::PromoteToCanonical,
            Self::Archive => LifecycleAction::Archive,
        }
    }
}
