use std::process::ExitCode;

use anyhow::Result;

use crate::memory;
use crate::output::{self, OutputFormat};
use crate::paths;

pub(crate) fn remember(args: crate::RememberArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;
    let report = memory::remember::run(
        &repo_path,
        args.profile.as_deref(),
        args.scope.map(crate::RememberScopeArg::into_scope),
        args.entry_type.as_str(),
        args.origin.as_str(),
        args.source_ref.as_deref(),
        args.dry_run,
        &args.content,
    )?;
    output::render_report(format, &report)
}

pub(crate) fn dispatch(command: crate::MemoryCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::MemoryCommand::Search(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::provider::search(
                &repo_path,
                args.profile.as_deref(),
                &args.query,
                Some(args.limit),
            )?;
            output::render_report(format, &report)
        }
        crate::MemoryCommand::Describe(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report =
                memory::provider::describe(&repo_path, args.profile.as_deref(), &args.native_id)?;
            output::render_report(format, &report)
        }
        crate::MemoryCommand::Expand(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::provider::expand(
                &repo_path,
                args.profile.as_deref(),
                &args.native_id,
                Some(args.limit),
            )?;
            output::render_report(format, &report)
        }
        crate::MemoryCommand::SyncStatus(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::provider::sync_status(&repo_path, args.profile.as_deref())?;
            output::render_report(format, &report)
        }
        crate::MemoryCommand::SourceMap(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::provider::source_map(&repo_path, args.profile.as_deref())?;
            output::render_report(format, &report)
        }
        crate::MemoryCommand::Evidence { command } => evidence(command, format),
        crate::MemoryCommand::Candidate { command } => candidate(command, format),
        crate::MemoryCommand::Compact(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let mode = memory::compact::parse_mode(memory::compact::CompactModeRequest {
                entry_id: args.entry.as_deref(),
                keep_id: args.keep.as_deref(),
                decay_class: args
                    .decay_class
                    .map(crate::MemoryDecayClassArg::into_decay_class),
                review: args
                    .review
                    .map(crate::MemoryCompactReviewArg::into_review_kind),
                remove: args.remove,
                write: args.write,
            })?;
            let report = memory::compact::run(
                &repo_path,
                args.profile.as_deref(),
                args.scope.into_scope(),
                mode,
                args.write,
                args.protected_write.into_state_options(),
                None,
            )?;
            output::render_report(format, &report)
        }
        crate::MemoryCommand::Promote(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let destination = match args.destination {
                crate::MemoryPromoteDestinationArg::BranchMemory => {
                    memory::promote::PromoteDestination::BranchMemory
                }
                crate::MemoryPromoteDestinationArg::RepoMemory => {
                    memory::promote::PromoteDestination::RepoMemory
                }
                crate::MemoryPromoteDestinationArg::ProfileMemory => {
                    memory::promote::PromoteDestination::ProfileMemory
                }
                crate::MemoryPromoteDestinationArg::ProjectTruth => {
                    let target_file = args.target_file.ok_or_else(|| {
                        anyhow::anyhow!(
                            "`--destination project-truth` requires `--target-file <path>`"
                        )
                    })?;
                    memory::promote::PromoteDestination::ProjectTruth { target_file }
                }
            };
            let report = memory::promote::run(
                &repo_path,
                args.profile.as_deref(),
                &args.entry,
                args.write,
                args.source_outcome
                    .map(crate::MemoryPromoteSourceOutcomeArg::into_source_outcome),
                destination,
                args.protected_write.into_state_options(),
                None,
            )?;
            output::render_report(format, &report)
        }
    }
}

fn evidence(command: crate::MemoryEvidenceCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::MemoryEvidenceCommand::Submit(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::evidence::submit(
                &repo_path,
                args.profile.as_deref(),
                memory::evidence::MemoryEvidenceEnvelope {
                    scope: args.scope.as_str().to_owned(),
                    entry_type: args.entry_type.as_str().to_owned(),
                    source_kind: args.source_kind.as_str().to_owned(),
                    summary: args.summary,
                    source_ref: args.source_ref,
                    host_reference: args.host.map(|host| {
                        memory::evidence::MemoryEvidenceHostReference {
                            host,
                            hook: args
                                .host_hook
                                .map(crate::HostHookArg::as_str)
                                .map(str::to_owned),
                            session_id: None,
                            run_id: None,
                            task_id: None,
                        }
                    }),
                    provider_reference: args.provider.map(|provider| {
                        memory::evidence::MemoryEvidenceProviderReference {
                            provider,
                            native_id: args.provider_ref,
                        }
                    }),
                },
                args.protected_write.into_state_options(),
                &memory::evidence::SubmitRuntimeHints::default(),
            )?;
            output::render_report(format, &report)
        }
    }
}

fn candidate(command: crate::MemoryCandidateCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::MemoryCandidateCommand::Admit(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::candidate::run_admit(
                &repo_path,
                args.profile.as_deref(),
                &args.entry,
                args.source_scope.into_source_scope(),
                args.destination.into_destination(),
                args.write,
                args.protected_write.into_state_options(),
                None,
            )?;
            output::render_report(format, &report)
        }
        crate::MemoryCandidateCommand::Extract(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = memory::evidence::extract_candidates(
                &repo_path,
                args.profile.as_deref(),
                args.evidence_id.as_deref(),
                args.limit,
                args.write,
                args.protected_write.into_state_options(),
            )?;
            output::render_report(format, &report)
        }
    }
}
