use std::process::ExitCode;

use anyhow::Result;

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

pub(crate) fn describe(format: OutputFormat) -> Result<ExitCode> {
    let report = commands::describe::run();
    output::render_report(format, &report)
}

pub(crate) fn check(args: crate::CheckArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;
    let report = commands::check::run(&repo_path)?;
    output::render_report(format, &report)
}

pub(crate) fn gc(args: crate::GcArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;
    let report = commands::gc::run(&repo_path)?;
    output::render_report(format, &report)
}

pub(crate) fn sync(args: crate::SyncArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;

    let mut sync_profile = commands::sync::SyncProfile::default();
    if paths::git::is_git_work_tree(&repo_path) {
        let profile = crate::profile::resolve(args.profile.as_deref())?;
        let layout = paths::state::StateLayout::resolve(&repo_path, profile)?;
        if let Ok(config) = layout.load_profile_config() {
            sync_profile.merge_config(&config.sync);
        }
    }

    let report = commands::sync::run(&repo_path, args.check, &sync_profile)?;
    output::render_report(format, &report)
}

pub(crate) fn marker(command: crate::MarkerCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::MarkerCommand::Repair(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = commands::marker::repair(&repo_path, args.yes)?;
            output::render_report(format, &report)
        }
    }
}

pub(crate) fn doctor(args: crate::DoctorArgs, format: OutputFormat) -> Result<ExitCode> {
    if args.severity.is_some() && format != OutputFormat::Json {
        anyhow::bail!("--severity requires --output json");
    }
    if args.fields.is_some() && format != OutputFormat::Json {
        anyhow::bail!("--fields requires --output json");
    }
    if let Some(fields) = &args.fields {
        output::validate_fields(&output::DOCTOR_FIELD_FILTER_SPEC, fields)?;
    }
    let repo_path = paths::cli::resolve(&args.path)?;
    let mut report = commands::doctor::run(
        &repo_path,
        None,
        commands::doctor::RunOptions {
            include_repo_native_checks: !args.skip_repo_native_checks,
        },
    )?;
    if let Some(severity) = args.severity {
        let min_status = match severity {
            crate::DoctorSeverityFilter::Fail => "fail",
            crate::DoctorSeverityFilter::Warn => "warn",
            crate::DoctorSeverityFilter::Pass => "pass",
        };
        report.filter_by_status(min_status);
    }
    match &args.fields {
        Some(fields) => {
            output::render_report_with_fields(report, fields, &output::DOCTOR_FIELD_FILTER_SPEC)
        }
        None => output::render_report(format, &report),
    }
}

pub(crate) fn consistency(args: crate::ConsistencyArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;
    let report = commands::consistency::run(&repo_path, args.profile.as_deref())?;
    output::render_report(format, &report)
}

pub(crate) fn drift(args: crate::DriftArgs, format: OutputFormat) -> Result<ExitCode> {
    let repo_path = paths::cli::resolve(&args.path)?;
    let report = commands::drift::run(&repo_path)?;
    output::render_report(format, &report)
}

pub(crate) fn hooks(command: crate::HooksCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::HooksCommand::Install(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = commands::hooks::install(&repo_path, args.force)?;
            output::render_report(format, &report)
        }
        crate::HooksCommand::Check(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let report = commands::hooks::check(&repo_path)?;
            output::render_report(format, &report)
        }
    }
}

pub(crate) fn skills(command: crate::SkillsCommand, format: OutputFormat) -> Result<ExitCode> {
    match command {
        crate::SkillsCommand::Install(args) => {
            let repo_path = paths::cli::resolve(&args.path)?;
            let global = !args.local;
            let report = commands::skills::run(&repo_path, global)?;
            output::render_report(format, &report)
        }
    }
}
