// SPDX-License-Identifier: AGPL-3.0-only OR Commercial

//! Derives `PUBLISHED_GALEON_ENGINE_VERSION` (the major.minor that scaffolded
//! projects pin against) so it cannot drift from the engine package this CLI
//! ships alongside. The generated file is included by `src/templates.rs` via
//! `include!`.
//!
//! Two source contexts:
//!
//! 1. **In-workspace build** (dev, CI, `cargo publish` source-side packaging):
//!    `crates/engine/Cargo.toml` is reachable. Resolve its effective version
//!    (handling `version.workspace = true` against the workspace root) and
//!    cross-check against `CARGO_PKG_VERSION`. The cross-check enforces the
//!    lockstep contract — `galeon-cli` ships with the engine package at the
//!    same workspace version — and fails the build before a mismatched pin
//!    can be published.
//!
//! 2. **Published tarball build** (e.g. `cargo install galeon-cli`): the
//!    engine manifest is not packaged, so we fall back to `CARGO_PKG_VERSION`.
//!    Cargo expands `version.workspace = true` to a literal at `cargo publish`
//!    time, so this value is the resolved workspace version — i.e. the engine
//!    version that was paired with this CLI release.

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

fn main() {
    let manifest_dir =
        PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
    let engine_manifest = manifest_dir.join("..").join("engine").join("Cargo.toml");
    let workspace_manifest = manifest_dir.join("..").join("..").join("Cargo.toml");

    println!("cargo:rerun-if-changed=build.rs");

    let cli_version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION");

    let version = if engine_manifest.exists() {
        println!("cargo:rerun-if-changed={}", engine_manifest.display());
        println!("cargo:rerun-if-changed={}", workspace_manifest.display());

        let engine_version = resolve_engine_package_version(&engine_manifest, &workspace_manifest);

        if engine_version != cli_version {
            panic!(
                "galeon-cli version `{cli_version}` (from CARGO_PKG_VERSION) disagrees with \
                 galeon-engine package version `{engine_version}` (from {}). These crates \
                 are released in lockstep at the workspace version; align the workspace \
                 `Cargo.toml` (or the engine package's `version`) before publishing — once \
                 published, downstream installs derive the engine pin from CARGO_PKG_VERSION \
                 and a mismatch ships silently.",
                engine_manifest.display()
            );
        }

        engine_version
    } else {
        cli_version
    };

    let (major, minor) = parse_major_minor(&version, &engine_manifest);

    let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR"));
    let dest = out_dir.join("published_galeon_engine_version.rs");

    let contents = format!(
        "// SPDX-License-Identifier: AGPL-3.0-only OR Commercial\n\
         // GENERATED by crates/galeon-cli/build.rs. Do not edit.\n\
         pub(crate) const PUBLISHED_GALEON_ENGINE_VERSION: &str = \"{major}.{minor}\";\n"
    );
    fs::write(&dest, contents).unwrap_or_else(|e| panic!("write {} failed: {e}", dest.display()));
}

fn resolve_engine_package_version(engine_manifest: &Path, workspace_manifest: &Path) -> String {
    let engine_text = fs::read_to_string(engine_manifest)
        .unwrap_or_else(|e| panic!("read {} failed: {e}", engine_manifest.display()));
    let engine_toml: toml::Value = toml::from_str(&engine_text)
        .unwrap_or_else(|e| panic!("parse {} failed: {e}", engine_manifest.display()));

    let version = engine_toml
        .get("package")
        .and_then(|p| p.get("version"))
        .unwrap_or_else(|| {
            panic!(
                "{} is missing `[package].version`",
                engine_manifest.display()
            )
        });

    if let Some(literal) = version.as_str() {
        return literal.to_owned();
    }

    let inherits_workspace = version
        .as_table()
        .and_then(|t| t.get("workspace"))
        .and_then(|w| w.as_bool())
        .unwrap_or(false);

    if !inherits_workspace {
        panic!(
            "unsupported `[package].version` form in {}: expected a string literal \
             or `version.workspace = true`",
            engine_manifest.display()
        );
    }

    let workspace_text = fs::read_to_string(workspace_manifest)
        .unwrap_or_else(|e| panic!("read {} failed: {e}", workspace_manifest.display()));
    let workspace_toml: toml::Value = toml::from_str(&workspace_text)
        .unwrap_or_else(|e| panic!("parse {} failed: {e}", workspace_manifest.display()));

    workspace_toml
        .get("workspace")
        .and_then(|w| w.get("package"))
        .and_then(|p| p.get("version"))
        .and_then(|v| v.as_str())
        .map(str::to_owned)
        .unwrap_or_else(|| {
            panic!(
                "{} is missing `[workspace.package].version` (required because {} declares \
                 `version.workspace = true`)",
                workspace_manifest.display(),
                engine_manifest.display(),
            )
        })
}

fn parse_major_minor(version: &str, source: &Path) -> (String, String) {
    let mut parts = version.split('.');
    let major = parts.next().unwrap_or_default();
    let minor = parts.next().unwrap_or_default();
    if major.is_empty() || minor.is_empty() {
        panic!(
            "version `{version}` from {} is missing a major.minor pair",
            source.display()
        );
    }
    if major.parse::<u64>().is_err() || minor.parse::<u64>().is_err() {
        panic!(
            "version `{version}` from {} is not numeric major.minor",
            source.display()
        );
    }
    (major.to_owned(), minor.to_owned())
}
