//! Decode with a dialect registry — shows hoisting, family resolution,
//! and default-filled params using an html-like dialect spec.
//!
//! Run: `cargo run --example tier1_dialect_registry`

// What this shows:
//   - Construct DialectSpec with FamilySpec + typed ParamSignature (WildcardWithTyped)
//   - Build an InMemoryRegistry and wire it to decode_t1_with_registry
//   - The `children:` content_slot param is hoisted into the body value tree
//   - A typed `class` param is type-checked; a default `id` is filled in
//   - The resolved `call.family` is now "tag" (not empty as in the no-registry case)

use dms::tier1::{
    decode_t1_with_registry, DialectSpec, FamilySpec, InMemoryRegistry,
    ParamMode, ParamSignature, TypeSpec, TypedParam, VersionStrategy,
};
use dms::{IndexMap, Value};

fn main() {
    // ── Build an html-like dialect ────────────────────────────────────────────
    // The "tag" family:
    //   - content_slot = "children" (gets hoisted into the body value tree)
    //   - WildcardWithTyped: unknown params pass; "class" must be a string
    //   - "id" has a default value of "" (filled when absent)
    let mut typed_params = IndexMap::new();
    typed_params.insert(
        "class".into(),
        TypedParam { ty: TypeSpec::String, required: false, default: None },
    );
    typed_params.insert(
        "id".into(),
        TypedParam {
            ty: TypeSpec::String,
            required: false,
            default: Some(Value::String("".into())),
        },
    );

    let tag_family = FamilySpec {
        name: "tag".into(),
        default_sigils: vec!["|".into()],
        // Decoration-only form substitutes an empty table.
        empty_default: Value::Table(Default::default()),
        // "children" param is the content slot; hoisted into the body on decode.
        content_slot: Some("children".into()),
        params: ParamSignature {
            mode: Some(ParamMode::WildcardWithTyped),
            typed: typed_params,
            required: vec![],
            positional: vec![],
        },
    };

    let dialect = DialectSpec {
        name: "html".into(),
        version: "1.0.0".into(),
        version_strategy: VersionStrategy::Caret,
        families: vec![tag_family],
        structs: vec![],
    };

    let registry = InMemoryRegistry::new().with(dialect);

    // ── Document that uses the dialect ────────────────────────────────────────
    // The `|tag(class: "nav", children: ["Home", "About"])` call on `menu`
    // will be hoisted: children: [...] moves into the body at the "menu" path.
    const SRC: &str = "\
+++
_dms_tier: 1
_dms_imports:
  + dialect: \"html\"
    version: \"1.0.0\"
    bind:
      \"|\": [\"tag\"]
+++
title: \"My Site\"
menu: |tag(class: \"nav\", children: [\"Home\", \"About\"])
";

    let doc = decode_t1_with_registry(SRC, &registry).expect("parse failed");

    println!("=== body after hoist pass ===");
    if let Value::Table(body) = &doc.t0.body {
        println!("  title = {:?}", body.get("title"));
        // After hoisting, body["menu"] holds the children list.
        println!("  menu  = {:?}", body.get("menu"));
    }

    println!("\n=== decorator sidecar ===");
    for entry in &doc.decorators {
        let path_str: Vec<String> = entry.path.iter().map(|s| format!("{s:?}")).collect();
        println!("  path: {}", path_str.join("."));
        for (sigil, calls) in &entry.decorators {
            for call in calls {
                println!(
                    "    sigil={sigil:?}  family={:?}  fn={:?}",
                    call.family, call.fn_name
                );
                // Params after hoisting: "children" key should be gone.
                for group in &call.params {
                    println!("    params = {group:?}");
                }
            }
        }
    }
}
