//! `openfut-import-fifa17` — real FIFA 17 profile import.
//!
//! This phase implements the read-only analysis and `--emit-content` (public
//! content pack + host catalog + private manifest). `--apply` is recognised but
//! deliberately refuses (and exits nonzero) so no Core DB / identity-store
//! writes happen before the apply phase lands.

use anyhow::{bail, Context, Result};
use std::collections::BTreeSet;
use std::path::Path;
use std::process::ExitCode;

use openfut_import_fifa17::apply::{apply, plan_apply, ApplyPaths};
use openfut_import_fifa17::{
    analyze, emit_content, fingerprint, load_roster, model::Profile, Entities,
};

fn main() -> ExitCode {
    match run() {
        Ok(code) => code,
        Err(e) => {
            eprintln!("import-fifa17: {e:#}");
            ExitCode::FAILURE
        }
    }
}

fn print_help() {
    eprintln!(
        "openfut-import-fifa17 --profile <fifa17_profile.json> [options]\n\
         \n\
         Read-only analysis of a real FIFA 17 Python profile for a faithful Core\n\
         import, with optional public content emission. Exits nonzero on unsafe blockers.\n\
         \n\
         --profile <path>          (required) source fifa17_profile.json\n\
         --roster <path>           roster.json for names (default fifa17-recon/data/roster.json)\n\
         --tables <dir>            tables dir (default fifa17-recon/data/tables)\n\
         --defer-conflict <rid>    explicitly approve deferring a reviewed resourceId conflict (repeatable)\n\
         --emit-content <dir>      write the production content pack + host catalog + private manifest\n\
         --apply                   apply across the identity store + Core (requires --emit-content)\n\
         --allow-deferred-players-for-staging   import despite deferred players (STAGED only; never default)\n\
         --core-bin <path>         openfut-core binary for the generic import transaction\n\
         --core-db <url>           DATABASE_URL for the target Core DB\n\
         --core-data <dir>         DATA_DIR base catalog for Core (default data)\n\
         --identity-store <path>   openfut-identity JSON store to seed\n"
    );
}

fn run() -> Result<ExitCode> {
    let mut profile_path: Option<String> = None;
    let mut roster_path = "fifa17-recon/data/roster.json".to_string();
    let mut tables_dir = "fifa17-recon/data/tables".to_string();
    let mut emit_dir: Option<String> = None;
    let mut approved: BTreeSet<i64> = BTreeSet::new();
    let mut do_apply = false;
    let mut allow_staging = false;
    let mut core_bin: Option<String> = None;
    let mut core_db: Option<String> = None;
    let mut core_data = "data".to_string();
    let mut identity_store: Option<String> = None;

    let mut args = std::env::args().skip(1);
    while let Some(a) = args.next() {
        match a.as_str() {
            "--profile" => profile_path = Some(args.next().context("--profile needs a path")?),
            "--roster" => roster_path = args.next().context("--roster needs a path")?,
            "--tables" => tables_dir = args.next().context("--tables needs a dir")?,
            "--emit-content" => emit_dir = Some(args.next().context("--emit-content needs a dir")?),
            "--defer-conflict" => {
                let rid = args.next().context("--defer-conflict needs a resourceId")?;
                approved.insert(
                    rid.parse::<i64>()
                        .with_context(|| format!("--defer-conflict {rid} is not an integer"))?,
                );
            }
            "--dry-run" => {}
            "--apply" => do_apply = true,
            "--allow-deferred-players-for-staging" => allow_staging = true,
            "--core-bin" => core_bin = Some(args.next().context("--core-bin needs a path")?),
            "--core-db" => core_db = Some(args.next().context("--core-db needs a DATABASE_URL")?),
            "--core-data" => core_data = args.next().context("--core-data needs a dir")?,
            "--identity-store" => {
                identity_store = Some(args.next().context("--identity-store needs a path")?)
            }
            "-h" | "--help" => {
                print_help();
                return Ok(ExitCode::SUCCESS);
            }
            other => bail!("unknown argument: {other} (try --help)"),
        }
    }
    let profile_path = profile_path.context("--profile <path> is required (try --help)")?;

    let raw =
        std::fs::read(&profile_path).with_context(|| format!("reading profile {profile_path}"))?;
    let profile = Profile::from_json_str(&String::from_utf8_lossy(&raw))?;
    let roster = load_roster(&roster_path)?;
    let entities = Entities::from_tables_dir(&tables_dir)?;

    let report = analyze(&profile, &roster, &entities, &approved);
    print!("{report}");

    if let Some(dir) = &emit_dir {
        if report.has_blockers() {
            bail!("not emitting content: unsafe blockers present (resolve them first)");
        }
        let fp = fingerprint(&raw);
        let sum = emit_content(&report, Path::new(dir), &fp)?;
        println!("\nEMITTED (fingerprint {fp})");
        println!(
            "  content pack : {} ({} definitions)",
            sum.content_pack.display(),
            sum.definitions
        );
        println!(
            "  host catalog : {} ({} entries)",
            sum.host_catalog.display(),
            sum.catalog_entries
        );
        println!(
            "  manifest     : {} (supported_instances={} deferred_instances={})",
            sum.manifest.display(),
            sum.supported_instances,
            sum.deferred_instances
        );
    }

    if do_apply {
        let dir = emit_dir
            .as_deref()
            .context("--apply requires --emit-content <dir> (it reads the emitted content pack)")?;
        if report.has_blockers() {
            bail!("not applying: unsafe blockers present (resolve them first)");
        }
        let raw_value: serde_json::Value =
            serde_json::from_slice(&raw).context("parse profile JSON for squad wire")?;
        let fp = fingerprint(&raw);
        let plan = plan_apply(&report, &raw_value, &fp)?;
        let base = Path::new(dir);
        let paths = ApplyPaths {
            core_bin: core_bin
                .context("--apply requires --core-bin <openfut-core binary>")?
                .into(),
            core_db_url: core_db.context("--apply requires --core-db <DATABASE_URL>")?,
            content_pack: base.join("content/fifa17-production-cards.json"),
            data_dir: core_data,
            identity_store: identity_store
                .context("--apply requires --identity-store <path>")?
                .into(),
            request_out: base.join("manifest/fifa17-core-import-request.json"),
            completion_out: base.join("manifest/fifa17-import-completion.json"),
        };
        let outcome = apply(&plan, &paths, allow_staging)?;
        println!(
            "\nAPPLIED (core_outcome={} staging={})",
            outcome.core_outcome, outcome.staging
        );
        println!(
            "  identity mappings installed: {} ; watermark: {}",
            outcome.mappings_installed, outcome.watermark
        );
        println!(
            "  supported instances: {} ; deferred instances: {}",
            outcome.supported_instances, outcome.deferred_instances
        );
        println!("  request    : {}", paths.request_out.display());
        println!("  completion : {}", paths.completion_out.display());
    }

    if report.has_blockers() {
        eprintln!("import-fifa17: FAILED — unsafe blockers present (see above)");
        Ok(ExitCode::FAILURE)
    } else {
        Ok(ExitCode::SUCCESS)
    }
}
