//! Byte-equivalence: the Rust KDL emitter (driven by the proc macros)
//! against `protocol/mizan-codegen/tests/fixtures/afi_ir.kdl` (canonical
//! Python-emitted reference).
//!
//! This is the Phase-2 verifier — the AFI fixture is authored against the
//! real consumer surface (`#[derive(Mizan)] / #[mizan::context] /
//! #[mizan::client]`), not hand-built static specs.

use mizan_core as mizan;
use mizan_core::prelude::*;
use mizan_core::RequestHandle;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

// ─── Output / shared types ──────────────────────────────────────────────────

#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct EchoOutput {
    pub message: String,
}

#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct WhoamiOutput {
    pub email: String,
    pub authenticated: bool,
}

#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct ProfileOutput {
    pub user_id: i64,
    pub name: String,
}

#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct OrderOutput {
    pub id: i64,
    pub user_id: i64,
    pub total: i64,
}

#[derive(Mizan, Serialize, Deserialize, Debug, Clone)]
pub struct StatusOutput {
    pub ok: bool,
}

#[mizan::context("user")]
pub struct UserCtx;

// ─── Fixture functions (mirroring tests/afi/fixture.py) ────────────────────

#[mizan::client]
pub async fn echo(_req: &RequestHandle<'_>, text: String) -> EchoOutput {
    EchoOutput {
        message: format!("echo: {text}"),
    }
}

#[mizan::client]
pub async fn whoami(_req: &RequestHandle<'_>) -> WhoamiOutput {
    WhoamiOutput {
        email: "anon@example.com".into(),
        authenticated: false,
    }
}

#[mizan::client(context = UserCtx)]
pub async fn user_profile(_req: &RequestHandle<'_>, user_id: i64) -> ProfileOutput {
    ProfileOutput {
        user_id,
        name: "placeholder".into(),
    }
}

#[mizan::client(context = UserCtx)]
pub async fn user_orders(_req: &RequestHandle<'_>, _user_id: i64) -> Vec<OrderOutput> {
    vec![]
}

#[mizan::client(affects = UserCtx)]
pub async fn update_profile(
    _req: &RequestHandle<'_>,
    _user_id: i64,
    _name: String,
) -> StatusOutput {
    StatusOutput { ok: true }
}

#[mizan::client]
pub async fn find_user(_req: &RequestHandle<'_>, _user_id: i64) -> Option<ProfileOutput> {
    None
}

#[mizan::client(merge = UserCtx)]
pub async fn rename_user(
    _req: &RequestHandle<'_>,
    user_id: i64,
    name: String,
) -> ProfileOutput {
    ProfileOutput { user_id, name }
}

// ─── The byte-equivalence test ──────────────────────────────────────────────

fn canonical_kdl_path() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../../protocol/mizan-codegen/tests/fixtures/afi_ir.kdl")
}

#[test]
fn build_ir_matches_canonical_afi_kdl() {
    let expected = std::fs::read_to_string(canonical_kdl_path()).expect("read canonical KDL");
    let actual = mizan_core::build_ir();

    if actual != expected {
        for (lineno, (a, b)) in actual.lines().zip(expected.lines()).enumerate() {
            if a != b {
                panic!(
                    "KDL diverges at line {}:\n  expected: {b:?}\n  actual:   {a:?}",
                    lineno + 1,
                );
            }
        }
        panic!(
            "KDL diverges in length: actual_len={} expected_len={}",
            actual.len(),
            expected.len(),
        );
    }
}
