use std::process::ExitCode;

use anyhow::Result;

use crate::commands;
use crate::host_adapter;
use crate::mcp;
use crate::output::{self, OutputFormat};
use crate::paths;

pub(crate) fn host(command: crate::HostCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::HostCommand::Apply(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = commands::host::apply(
                &repo_path,
                args.profile.as_deref(),
                args.host.map(crate::RepoHostArg::into_host),
                args.force,
                args.with_launcher,
            )?;
            output::render_report(format, &report)
        }
    }
}

pub(crate) fn host_hook(args: crate::HostHookRunArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;
    let profile_name = crate::profile::resolve(args.profile.as_deref())?;
    let host = crate::resolve_host_adapter_name(args.host.clone())?;
    let hook = args.hook.as_str();
    let hook_protocol_hook = if format == OutputFormat::HookProtocol {
        if host != "claude" {
            anyhow::bail!(
                "--output hook-protocol is only implemented for --host claude; host `{host}` is not supported in v1"
            );
        }
        let cli_hook = hook.replace('_', "-");
        commands::host::claude_event_for_hook(&cli_hook)?;
        Some(cli_hook)
    } else {
        None
    };
    let runtime_hints = crate::host_hook_runtime_hints(&args);
    let session_start = crate::host_hook_session_start_options(&args);
    let capture = crate::build_host_hook_capture(&args, &host, &runtime_hints)?;
    let report = host_adapter::run_hook(
        &repo_path,
        profile_name.as_str(),
        &host,
        hook,
        session_start,
        args.diagnostics,
        args.host_total_context_chars,
        args.attempt_outcome
            .map(crate::AttemptOutcomeArg::into_state_outcome),
        capture,
        args.protected_write.into_state_options(),
        runtime_hints,
    )?;

    if let Some(cli_hook) = hook_protocol_hook {
        let payload = commands::host::build_hook_protocol_payload(
            &cli_hook,
            commands::host::HookProtocolInputs {
                context: report.context(),
                session_boundary_action: report.session_boundary_action(),
            },
        )?;
        println!("{}", serde_json::to_string_pretty(&payload)?);
        return Ok(ExitCode::SUCCESS);
    }

    output::render_report(format, &report)
}

pub(crate) fn telemetry(
    command: crate::TelemetryCommand,
    format: OutputFormat,
) -> Result<ExitCode> {
    match command {
        crate::TelemetryCommand::Report(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report =
                commands::telemetry::run_report(&repo_path, args.profile.as_deref(), args.limit)?;
            output::render_report(format, &report)
        }
    }
}

pub(crate) fn mcp_serve() -> Result<ExitCode> {
    mcp::serve()?;
    Ok(ExitCode::SUCCESS)
}

#[cfg(feature = "daemon")]
pub(crate) fn daemon(args: crate::DaemonArgs) -> Result<ExitCode> {
    let rt = tokio::runtime::Runtime::new()
        .map_err(|e| anyhow::anyhow!("failed to create tokio runtime: {e}"))?;
    rt.block_on(mcp::daemon::serve(
        &args.host,
        args.port,
        args.profile,
        args.cors,
    ))?;
    Ok(ExitCode::SUCCESS)
}
