use std::path::{Path, PathBuf};

use crate::cli::{
    CrystallizeArgs, CrystallizeCommand, CrystallizeIngestArgs, CrystallizeShadowArgs,
    CrystallizeValidateArgs,
};

pub(crate) fn run(args: CrystallizeArgs) -> Result<(), String> {
    match args.command {
        Some(CrystallizeCommand::Validate(validate)) => run_validate(validate),
        Some(CrystallizeCommand::Shadow(shadow)) => run_shadow(shadow),
        Some(CrystallizeCommand::Ingest(ingest)) => run_ingest(*ingest),
        None => run_mine(args),
    }
}

fn run_mine(args: CrystallizeArgs) -> Result<(), String> {
    let from = args
        .from
        .as_deref()
        .ok_or_else(|| "--from is required when no subcommand is given".to_string())?;
    let out = args
        .out
        .as_deref()
        .ok_or_else(|| "--out is required when no subcommand is given".to_string())?;
    let report = args
        .report
        .as_deref()
        .ok_or_else(|| "--report is required when no subcommand is given".to_string())?;

    let trace_dir = PathBuf::from(from);
    let traces = harn_vm::orchestration::load_crystallization_traces_from_dir(&trace_dir)
        .map_err(|error| error.to_string())?;
    let mut bundle_traces = traces.clone();
    let mut shadow_traces = Vec::new();
    for shadow_from in &args.shadow_from {
        let mut loaded =
            harn_vm::orchestration::load_crystallization_traces_from_dir(Path::new(shadow_from))
                .map_err(|error| error.to_string())?;
        bundle_traces.extend(loaded.clone());
        shadow_traces.append(&mut loaded);
    }
    let artifacts = harn_vm::orchestration::crystallize_traces(
        traces,
        harn_vm::orchestration::CrystallizeOptions {
            min_examples: args.min_examples,
            shadow_traces,
            promotion_min_confidence: args.promotion_min_confidence,
            workflow_name: args.workflow_name.clone(),
            package_name: args.package_name.clone(),
            author: args.author.clone(),
            approver: args.approver.clone(),
            eval_pack_link: args.eval_pack.clone(),
        },
    )
    .map_err(|error| error.to_string())?;

    let bundle = if args.bundle.is_some() {
        Some(
            harn_vm::orchestration::build_crystallization_bundle(
                artifacts.clone(),
                &bundle_traces,
                harn_vm::orchestration::BundleOptions {
                    external_key: args.bundle_external_key.clone(),
                    title: args.bundle_title.clone(),
                    team: args.bundle_team.clone(),
                    repo: args.bundle_repo.clone(),
                    risk_level: args.bundle_risk_level.clone(),
                    rollout_policy: args.bundle_rollout_policy.clone(),
                },
            )
            .map_err(|error| error.to_string())?,
        )
    } else {
        None
    };

    let report_struct = harn_vm::orchestration::write_crystallization_artifacts(
        artifacts,
        Path::new(out),
        Path::new(report),
        args.eval_pack.as_deref().map(Path::new),
    )
    .map_err(|error| error.to_string())?;

    let selected = report_struct
        .selected_candidate_id
        .as_deref()
        .unwrap_or("none");
    println!(
        "Crystallization: selected={selected} candidates={} rejected={} traces={}",
        report_struct.candidates.len(),
        report_struct.rejected_candidates.len(),
        report_struct.source_trace_count
    );
    println!("Workflow: {out}");
    println!("Report: {report}");
    if let Some(path) = args.eval_pack.as_deref() {
        println!("Eval pack: {path}");
    }

    if let (Some(bundle_dir), Some(bundle)) = (args.bundle.as_deref(), bundle) {
        let manifest =
            harn_vm::orchestration::write_crystallization_bundle(&bundle, Path::new(bundle_dir))
                .map_err(|error| error.to_string())?;
        println!(
            "Bundle: {bundle_dir} (kind={:?} schema_version={} fixtures={})",
            manifest.kind,
            manifest.schema_version,
            manifest.fixtures.len()
        );
    }

    if report_struct.selected_candidate_id.is_none() {
        return Err("no safe crystallization candidate was proposed".to_string());
    }

    Ok(())
}

fn run_validate(args: CrystallizeValidateArgs) -> Result<(), String> {
    let validation =
        harn_vm::orchestration::validate_crystallization_bundle(Path::new(&args.bundle_dir))
            .map_err(|error| error.to_string())?;
    println!(
        "Bundle: {} (schema={} schema_version={} kind={:?} candidate_id={})",
        validation.bundle_dir,
        if validation.schema.is_empty() {
            "(unknown)".to_string()
        } else {
            validation.schema.clone()
        },
        validation.schema_version,
        validation.kind,
        if validation.candidate_id.is_empty() {
            "-"
        } else {
            validation.candidate_id.as_str()
        }
    );
    println!(
        "Checks: manifest={} workflow={} report={} eval_pack={} skill={} fixtures={} redaction={}",
        ok_label(validation.manifest_ok),
        ok_label(validation.workflow_ok),
        ok_label(validation.report_ok),
        ok_label(validation.eval_pack_ok),
        ok_label(validation.skill_ok),
        ok_label(validation.fixtures_ok),
        ok_label(validation.redaction_ok),
    );
    if validation.problems.is_empty() {
        println!("OK");
        Ok(())
    } else {
        for problem in &validation.problems {
            eprintln!("- {problem}");
        }
        Err(format!(
            "bundle validation failed with {} problem(s)",
            validation.problems.len()
        ))
    }
}

fn run_shadow(args: CrystallizeShadowArgs) -> Result<(), String> {
    let (manifest, shadow) =
        harn_vm::orchestration::shadow_replay_bundle(Path::new(&args.bundle_dir))
            .map_err(|error| error.to_string())?;
    println!(
        "Shadow replay: bundle={} candidate_id={} compared={} pass={}",
        args.bundle_dir, manifest.candidate_id, shadow.compared_traces, shadow.pass
    );
    if !shadow.failures.is_empty() {
        for failure in &shadow.failures {
            eprintln!("- {failure}");
        }
        return Err(format!(
            "shadow replay failed with {} failure(s)",
            shadow.failures.len()
        ));
    }
    Ok(())
}

fn ok_label(value: bool) -> &'static str {
    if value {
        "ok"
    } else {
        "fail"
    }
}

fn run_ingest(args: CrystallizeIngestArgs) -> Result<(), String> {
    let from = PathBuf::from(&args.from);
    let bundle_dir = PathBuf::from(&args.bundle);
    let workflow_name = args
        .workflow_name
        .clone()
        .or_else(|| Some("release_harn".to_string()));
    let package_name = args
        .package_name
        .clone()
        .or_else(|| Some("release-harn".to_string()));

    let (artifacts, fixture, trace) = harn_vm::orchestration::ingest_release_fixture(
        &from,
        harn_vm::orchestration::CrystallizeOptions {
            min_examples: 1,
            workflow_name,
            package_name,
            author: args.author.clone(),
            approver: args.approver.clone(),
            eval_pack_link: args.eval_pack.clone(),
            ..harn_vm::orchestration::CrystallizeOptions::default()
        },
    )
    .map_err(|error| error.to_string())?;

    let bundle = harn_vm::orchestration::build_crystallization_bundle(
        artifacts.clone(),
        std::slice::from_ref(&trace),
        harn_vm::orchestration::BundleOptions {
            external_key: args.bundle_external_key.clone(),
            title: args.bundle_title.clone(),
            team: args.bundle_team.clone(),
            // `release.repo` is a local filesystem path, not a github-style
            // identifier. Leave `repo` unset unless the caller explicitly
            // provides one via --bundle-repo.
            repo: args.bundle_repo.clone(),
            risk_level: args.bundle_risk_level.clone(),
            rollout_policy: args.bundle_rollout_policy.clone(),
        },
    )
    .map_err(|error| error.to_string())?;

    if let Some(out) = args.out.as_deref() {
        let report_path = args.report.clone().unwrap_or_else(|| {
            // Default: place report.json next to the standalone workflow.
            Path::new(out)
                .with_file_name("release-candidate-report.json")
                .display()
                .to_string()
        });
        harn_vm::orchestration::write_crystallization_artifacts(
            artifacts.clone(),
            Path::new(out),
            Path::new(&report_path),
            args.eval_pack.as_deref().map(Path::new),
        )
        .map_err(|error| error.to_string())?;
        println!("Workflow: {out}");
        println!("Report: {report_path}");
        if let Some(eval_path) = args.eval_pack.as_deref() {
            println!("Eval pack: {eval_path}");
        }
    }

    let manifest = harn_vm::orchestration::write_crystallization_bundle(&bundle, &bundle_dir)
        .map_err(|error| error.to_string())?;

    let report = &artifacts.report;
    let candidate_id = report.selected_candidate_id.as_deref().unwrap_or("(none)");
    println!(
        "Ingest: from={} run_id={} version={}->{} candidate={}",
        args.from,
        fixture.manifest.run_id,
        fixture.manifest.release.current_version,
        fixture.manifest.release.next_version,
        candidate_id
    );
    println!(
        "Bundle: {} (kind={:?} schema_version={} fixtures={})",
        args.bundle,
        manifest.kind,
        manifest.schema_version,
        manifest.fixtures.len()
    );
    if let Some(summary) = &report.segment_summary {
        println!(
            "Segments: deterministic={} agentic={} (review-required: {})",
            summary.deterministic_count,
            summary.agentic_count,
            summary.requires_human_review.len()
        );
    }
    if let Some(recovery) = &report.recovery_summary {
        println!(
            "Recovery: shell_failures={} recovery_runs={} fed_into_agent={}",
            recovery.shell_failures_seen,
            recovery.recovery_advice_runs,
            recovery.failures_fed_into_agent
        );
    }

    if report.selected_candidate_id.is_none() {
        return Err("ingest produced no safe crystallization candidate".to_string());
    }

    if args.shadow {
        let (shadow_manifest, shadow) = harn_vm::orchestration::shadow_replay_bundle(&bundle_dir)
            .map_err(|error| error.to_string())?;
        println!(
            "Shadow replay: candidate_id={} compared={} pass={}",
            shadow_manifest.candidate_id, shadow.compared_traces, shadow.pass
        );
        if !shadow.pass {
            for failure in &shadow.failures {
                eprintln!("- {failure}");
            }
            return Err(format!(
                "shadow replay failed with {} failure(s)",
                shadow.failures.len()
            ));
        }
    }

    Ok(())
}
