use std::path::Path;
use std::process::ExitCode;

use anyhow::Result;
use serde::Serialize;

use crate::output::CommandReport;
use crate::repo::marker as repo_marker;

#[derive(Serialize)]
pub(crate) struct MarkerRepairReport {
    command: &'static str,
    ok: bool,
    repaired: bool,
    dry_run: bool,
    message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    preview: Option<repo_marker::MarkerRepairPreview>,
    #[serde(skip_serializing_if = "Option::is_none")]
    repaired_marker: Option<String>,
}

impl CommandReport for MarkerRepairReport {
    fn exit_code(&self) -> ExitCode {
        if self.ok {
            ExitCode::SUCCESS
        } else {
            ExitCode::from(1)
        }
    }

    fn render_text(&self) {
        println!("{}", self.message);
        if let Some(preview) = &self.preview {
            println!(
                "repair: {} version {} -> {}; preserve {} as `{}`",
                preview.marker_path,
                preview.before_version,
                preview.after_version,
                preview.after_field,
                preview.preserved_project_id
            );
            if self.dry_run {
                println!("No changes written. Re-run with `--yes` to rewrite the marker.");
            }
        }
    }
}

pub(crate) fn repair(repo_root: &Path, yes: bool) -> Result<MarkerRepairReport> {
    if yes {
        match repo_marker::repair_legacy_marker(repo_root)? {
            Some(outcome) => Ok(MarkerRepairReport {
                command: "marker repair",
                ok: true,
                repaired: true,
                dry_run: false,
                message: "repaired legacy marker from repo_id/version=1 to project_id/version=2"
                    .to_owned(),
                preview: Some(outcome.preview),
                repaired_marker: Some(outcome.repaired_marker),
            }),
            None => Ok(MarkerRepairReport {
                command: "marker repair",
                ok: true,
                repaired: false,
                dry_run: false,
                message: "no legacy repo_id/version=1 marker repair was needed".to_owned(),
                preview: None,
                repaired_marker: None,
            }),
        }
    } else {
        match repo_marker::legacy_repair_preview(repo_root)? {
            Some(preview) => Ok(MarkerRepairReport {
                command: "marker repair",
                ok: true,
                repaired: false,
                dry_run: true,
                message: "legacy marker repair is available".to_owned(),
                preview: Some(preview),
                repaired_marker: None,
            }),
            None => Ok(MarkerRepairReport {
                command: "marker repair",
                ok: true,
                repaired: false,
                dry_run: true,
                message: "no legacy repo_id/version=1 marker repair was needed".to_owned(),
                preview: None,
                repaired_marker: None,
            }),
        }
    }
}
