use crate::desktop::{
    DesktopContextResponse, DesktopDaemonRequest, DesktopErrorEnvelope, DesktopHistoryResponse,
    DesktopImportSessionRequest, DesktopImportSessionResponse, DesktopLifecycleActionDto,
    DesktopMemoryActionRequest, DesktopMemoryDraftRequest, DesktopMetadataDto,
    DesktopPromptOptimizeRequest, DesktopPromptOptimizeResponse, DesktopRecordLookupRequest,
    DesktopRecordResponse, DesktopRouteRequest, DesktopService, DesktopSessionActionRequest,
    DesktopSessionActionResponse, DesktopSessionBrowserRequest, DesktopSessionBrowserResponse,
    DesktopSessionDetailRequest, DesktopSessionDetailResponse, DesktopStatusRequest,
    DesktopStatusResponse, DesktopWakeupRequest, DesktopWakeupResponse, DesktopWikiIndexRequest,
    DesktopWikiIndexResponse, DesktopWikiLintRequest, DesktopWikiLintResponse,
    DesktopWorkbenchRequest, DesktopWorkbenchResponse, DesktopWriteResponse,
};
use crate::domain::{MemoryScope, OutputFormat, TargetTool, WakeupProfile};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

pub type TauriCommandResult<T> = Result<T, DesktopErrorEnvelope>;

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriContextCommand {
    pub config_path: PathBuf,
    pub vault_root_override: Option<PathBuf>,
    pub cwd: PathBuf,
    pub task: String,
    #[serde(default)]
    pub files: Vec<String>,
    pub target: TargetTool,
    pub format: OutputFormat,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriWakeupCommand {
    pub config_path: PathBuf,
    pub vault_root_override: Option<PathBuf>,
    pub cwd: PathBuf,
    pub task: String,
    #[serde(default)]
    pub files: Vec<String>,
    pub target: TargetTool,
    pub profile: WakeupProfile,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriPromptOptimizeCommand {
    pub config_path: PathBuf,
    pub vault_root_override: Option<PathBuf>,
    pub cwd: PathBuf,
    pub task: String,
    #[serde(default)]
    pub files: Vec<String>,
    pub target: TargetTool,
    pub profile: WakeupProfile,
    pub provider: Option<String>,
    pub session_id: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriDaemonCommand {
    pub enabled: bool,
    pub daemon_bin: Option<PathBuf>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriWorkbenchCommand {
    pub config_path: PathBuf,
    pub daemon: Option<TauriDaemonCommand>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriRecordCommand {
    pub config_path: PathBuf,
    pub record_id: String,
    pub daemon: Option<TauriDaemonCommand>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriSessionBrowserCommand {
    pub config_path: PathBuf,
    pub page: Option<usize>,
    pub per_page: Option<usize>,
    pub provider: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriSessionDetailCommand {
    pub config_path: PathBuf,
    pub session_id: String,
    pub message_offset: Option<usize>,
    pub message_limit: Option<usize>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriSessionActionCommand {
    pub config_path: PathBuf,
    pub session_id: String,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriStatusCommand {
    pub config_path: PathBuf,
    pub vault_root_override: Option<PathBuf>,
    pub cwd: PathBuf,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriImportSessionCommand {
    pub config_path: PathBuf,
    pub provider: String,
    pub session_id: String,
    #[serde(default)]
    pub apply: bool,
    #[serde(default)]
    pub actor: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriMemoryDraftCommand {
    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>,
    #[serde(default)]
    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, Deserialize, Serialize, PartialEq, Eq)]
pub struct TauriMemoryActionCommand {
    pub config_path: PathBuf,
    pub record_id: String,
    pub action: DesktopLifecycleActionDto,
    #[serde(default)]
    pub metadata: DesktopMetadataDto,
}

pub fn desktop_run_context(
    command: TauriContextCommand,
) -> TauriCommandResult<DesktopContextResponse> {
    DesktopService::new().run_context(command.into_request())
}

pub fn desktop_build_wakeup(
    command: TauriWakeupCommand,
) -> TauriCommandResult<DesktopWakeupResponse> {
    DesktopService::new().build_wakeup(command.into_request())
}

pub fn desktop_optimize_prompt(
    command: TauriPromptOptimizeCommand,
) -> TauriCommandResult<DesktopPromptOptimizeResponse> {
    DesktopService::new().optimize_prompt(command.into_request())
}

pub fn desktop_load_workbench(
    command: TauriWorkbenchCommand,
) -> TauriCommandResult<DesktopWorkbenchResponse> {
    DesktopService::new().load_workbench(command.into_request())
}

pub fn desktop_get_record(
    command: TauriRecordCommand,
) -> TauriCommandResult<Option<DesktopRecordResponse>> {
    DesktopService::new().get_record(command.into_request())
}

pub fn desktop_get_history(
    command: TauriRecordCommand,
) -> TauriCommandResult<DesktopHistoryResponse> {
    DesktopService::new().get_history(command.into_request())
}

pub fn desktop_browse_sessions(
    command: TauriSessionBrowserCommand,
) -> TauriCommandResult<DesktopSessionBrowserResponse> {
    DesktopService::new().browse_sessions(command.into_request())
}

pub fn desktop_get_session(
    command: TauriSessionDetailCommand,
) -> TauriCommandResult<Option<DesktopSessionDetailResponse>> {
    DesktopService::new().get_session(command.into_request())
}

pub fn desktop_collect_status(
    command: TauriStatusCommand,
) -> TauriCommandResult<DesktopStatusResponse> {
    DesktopService::new().collect_status(command.into_request())
}

pub fn desktop_continue_session(
    command: TauriSessionActionCommand,
) -> TauriCommandResult<DesktopSessionActionResponse> {
    DesktopService::new().continue_session(command.into_request())
}

pub fn desktop_delete_session(
    command: TauriSessionActionCommand,
) -> TauriCommandResult<DesktopSessionActionResponse> {
    DesktopService::new().delete_session(command.into_request())
}

pub fn desktop_record_manual(
    command: TauriMemoryDraftCommand,
) -> TauriCommandResult<DesktopWriteResponse> {
    DesktopService::new().record_manual(command.into_request())
}

pub fn desktop_propose_memory(
    command: TauriMemoryDraftCommand,
) -> TauriCommandResult<DesktopWriteResponse> {
    DesktopService::new().propose_memory(command.into_request())
}

pub fn desktop_apply_memory_action(
    command: TauriMemoryActionCommand,
) -> TauriCommandResult<DesktopWriteResponse> {
    DesktopService::new().apply_memory_action(command.into_request())
}

pub fn desktop_import_session(
    command: TauriImportSessionCommand,
) -> TauriCommandResult<DesktopImportSessionResponse> {
    DesktopService::new().import_session(command.into_request())
}

pub fn desktop_wiki_lint(
    request: DesktopWikiLintRequest,
) -> TauriCommandResult<DesktopWikiLintResponse> {
    DesktopService::new().wiki_lint(request)
}

pub fn desktop_read_wiki_index(
    request: DesktopWikiIndexRequest,
) -> TauriCommandResult<DesktopWikiIndexResponse> {
    DesktopService::new().read_wiki_index(request)
}

impl TauriContextCommand {
    fn into_request(self) -> DesktopRouteRequest {
        DesktopRouteRequest {
            config_path: self.config_path,
            vault_root_override: self.vault_root_override,
            cwd: self.cwd,
            task: self.task,
            files: self.files,
            target: self.target,
            format: self.format,
        }
    }
}

impl TauriWakeupCommand {
    fn into_request(self) -> DesktopWakeupRequest {
        DesktopWakeupRequest {
            config_path: self.config_path,
            vault_root_override: self.vault_root_override,
            cwd: self.cwd,
            task: self.task,
            files: self.files,
            target: self.target,
            profile: self.profile,
        }
    }
}

impl TauriPromptOptimizeCommand {
    fn into_request(self) -> DesktopPromptOptimizeRequest {
        DesktopPromptOptimizeRequest {
            config_path: self.config_path,
            vault_root_override: self.vault_root_override,
            cwd: self.cwd,
            task: self.task,
            files: self.files,
            target: self.target,
            profile: self.profile,
            provider: self.provider,
            session_id: self.session_id,
        }
    }
}

impl TauriWorkbenchCommand {
    fn into_request(self) -> DesktopWorkbenchRequest {
        DesktopWorkbenchRequest {
            config_path: self.config_path,
            daemon: self.daemon.map(TauriDaemonCommand::into_request),
        }
    }
}

impl TauriRecordCommand {
    fn into_request(self) -> DesktopRecordLookupRequest {
        DesktopRecordLookupRequest {
            config_path: self.config_path,
            record_id: self.record_id,
            daemon: self.daemon.map(TauriDaemonCommand::into_request),
        }
    }
}

impl TauriDaemonCommand {
    fn into_request(self) -> DesktopDaemonRequest {
        DesktopDaemonRequest {
            enabled: self.enabled,
            daemon_bin: self.daemon_bin,
        }
    }
}

impl TauriSessionBrowserCommand {
    fn into_request(self) -> DesktopSessionBrowserRequest {
        DesktopSessionBrowserRequest {
            config_path: self.config_path,
            page: self.page.unwrap_or(1),
            per_page: self.per_page.unwrap_or(10),
            provider: self.provider,
        }
    }
}

impl TauriSessionDetailCommand {
    fn into_request(self) -> DesktopSessionDetailRequest {
        DesktopSessionDetailRequest {
            config_path: self.config_path,
            session_id: self.session_id,
            message_offset: self.message_offset,
            message_limit: self.message_limit,
        }
    }
}

impl TauriStatusCommand {
    fn into_request(self) -> DesktopStatusRequest {
        DesktopStatusRequest {
            config_path: self.config_path,
            vault_root_override: self.vault_root_override,
            cwd: self.cwd,
        }
    }
}

impl TauriSessionActionCommand {
    fn into_request(self) -> DesktopSessionActionRequest {
        DesktopSessionActionRequest {
            config_path: self.config_path,
            session_id: self.session_id,
        }
    }
}

impl TauriMemoryDraftCommand {
    fn into_request(self) -> DesktopMemoryDraftRequest {
        DesktopMemoryDraftRequest {
            config_path: self.config_path,
            title: self.title,
            summary: self.summary,
            memory_type: self.memory_type,
            scope: self.scope,
            source_ref: self.source_ref,
            project_id: self.project_id,
            user_id: self.user_id,
            sensitivity: self.sensitivity,
            metadata: self.metadata,
            entities: self.entities,
            tags: self.tags,
            triggers: self.triggers,
            related_files: self.related_files,
            related_records: self.related_records,
            supersedes: self.supersedes,
            applies_to: self.applies_to,
            valid_until: self.valid_until,
        }
    }
}

impl TauriMemoryActionCommand {
    fn into_request(self) -> DesktopMemoryActionRequest {
        DesktopMemoryActionRequest {
            config_path: self.config_path,
            record_id: self.record_id,
            action: self.action,
            metadata: self.metadata,
        }
    }
}

impl TauriImportSessionCommand {
    fn into_request(self) -> DesktopImportSessionRequest {
        DesktopImportSessionRequest {
            config_path: self.config_path,
            provider: self.provider,
            session_id: self.session_id,
            apply: self.apply,
            actor: self.actor,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{
        TauriContextCommand, TauriDaemonCommand, TauriMemoryActionCommand, TauriMemoryDraftCommand,
        TauriRecordCommand, TauriWakeupCommand, TauriWorkbenchCommand, desktop_apply_memory_action,
        desktop_build_wakeup, desktop_get_history, desktop_get_record, desktop_load_workbench,
        desktop_propose_memory, desktop_run_context,
    };
    use crate::desktop::{DesktopLifecycleActionDto, DesktopMetadataDto};
    use crate::domain::{
        MemoryLifecycleState, MemoryScope, OutputFormat, TargetTool, WakeupProfile,
    };
    use std::fs;
    use tempfile::tempdir;

    fn setup_workspace() -> (tempfile::TempDir, std::path::PathBuf, std::path::PathBuf) {
        let temp = tempdir().unwrap();
        let vault_root = temp.path().join("vault");
        let repo_root = temp.path().join("repo");
        fs::create_dir_all(vault_root.join("10-Projects")).unwrap();
        fs::create_dir_all(&repo_root).unwrap();
        fs::write(
            vault_root.join("10-Projects/spool.md"),
            "---\ntitle: spool\nmemory_type: project\n---\n\nDesktop tauri command test note.\n",
        )
        .unwrap();
        let config_path = temp.path().join("spool.toml");
        fs::write(
            &config_path,
            format!(
                "[vault]\nroot = \"{}\"\n\n[output]\ndefault_format = \"markdown\"\nmax_chars = 4000\nmax_notes = 8\n\n[[projects]]\nid = \"spool\"\nname = \"spool\"\nrepo_paths = [\"{}\"]\nnote_roots = [\"10-Projects\"]\n",
                vault_root.display(),
                repo_root.display(),
            ),
        )
        .unwrap();
        (temp, config_path, repo_root)
    }

    #[test]
    fn tauri_commands_should_parse_inputs_and_run_context_and_wakeup() {
        let (_temp, config_path, repo_root) = setup_workspace();

        let context = desktop_run_context(TauriContextCommand {
            config_path: config_path.clone(),
            vault_root_override: None,
            cwd: repo_root.clone(),
            task: "summarize current project context".to_string(),
            files: vec!["src/lib.rs".to_string(), "src/main.rs".to_string()],
            target: TargetTool::Claude,
            format: OutputFormat::Markdown,
        })
        .unwrap();
        assert_eq!(context.bundle.route.debug.note_count, 1);

        let wakeup = desktop_build_wakeup(TauriWakeupCommand {
            config_path,
            vault_root_override: None,
            cwd: repo_root,
            task: "prepare restart packet".to_string(),
            files: Vec::new(),
            target: TargetTool::Claude,
            profile: WakeupProfile::Project,
        })
        .unwrap();
        assert_eq!(wakeup.packet.profile, WakeupProfile::Project);
    }

    #[test]
    fn tauri_commands_should_cover_lifecycle_read_and_write_flows() {
        let (_temp, config_path, _repo_root) = setup_workspace();

        let created = desktop_propose_memory(TauriMemoryDraftCommand {
            config_path: config_path.clone(),
            title: "测试偏好".to_string(),
            summary: "先 smoke 再收口".to_string(),
            memory_type: "workflow".to_string(),
            scope: MemoryScope::User,
            source_ref: "session:1".to_string(),
            project_id: None,
            user_id: Some("long".to_string()),
            sensitivity: None,
            metadata: DesktopMetadataDto {
                actor: Some("desktop".to_string()),
                reason: Some("captured from tauri command".to_string()),
                evidence_refs: vec!["session:1".to_string(), "obsidian://workflow".to_string()],
            },
            entities: Vec::new(),
            tags: Vec::new(),
            triggers: Vec::new(),
            related_files: Vec::new(),
            related_records: Vec::new(),
            supersedes: None,
            applies_to: Vec::new(),
            valid_until: None,
        })
        .unwrap();
        assert_eq!(created.entry.record.state, MemoryLifecycleState::Candidate);

        let workbench = desktop_load_workbench(TauriWorkbenchCommand {
            config_path: config_path.clone(),
            daemon: Some(TauriDaemonCommand {
                enabled: false,
                daemon_bin: None,
            }),
        })
        .unwrap();
        assert_eq!(workbench.snapshot.pending_review.len(), 1);

        let record = desktop_get_record(TauriRecordCommand {
            config_path: config_path.clone(),
            record_id: created.entry.record_id.clone(),
            daemon: None,
        })
        .unwrap()
        .unwrap();
        assert_eq!(record.record.record.title, "测试偏好");

        let action = desktop_apply_memory_action(TauriMemoryActionCommand {
            config_path: config_path.clone(),
            record_id: created.entry.record_id.clone(),
            action: DesktopLifecycleActionDto::Accept,
            metadata: DesktopMetadataDto::default(),
        })
        .unwrap();
        assert_eq!(action.entry.record.state, MemoryLifecycleState::Accepted);

        let history = desktop_get_history(TauriRecordCommand {
            config_path,
            record_id: created.entry.record_id,
            daemon: None,
        })
        .unwrap();
        assert_eq!(history.history.len(), 2);
    }
}
