using System.Text.Json.Serialization;

namespace Theriapolis.Core.Data;

/// <summary>
/// Immutable NPC stat-block template loaded from npc_templates.json.
/// Phase 5 instantiates one per <see cref="Tactical.SpawnKind"/> per
/// chunk, with the actual template id chosen via the per-zone lookup
/// table on <see cref="NpcTemplateContent.SpawnKindToTemplateByZone"/>.
/// </summary>
public sealed record NpcTemplateDef
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    [JsonPropertyName("name")]
    public string Name { get; init; } = "";

    /// <summary>Body size category, snake_case (small / medium / medium_large / large).</summary>
    [JsonPropertyName("size")]
    public string Size { get; init; } = "medium";

    /// <summary>STR/DEX/CON/INT/WIS/CHA absolute values (10 = average).</summary>
    [JsonPropertyName("ability_scores")]
    public Dictionary<string, int> AbilityScores { get; init; } = new();

    [JsonPropertyName("hp")]
    public int Hp { get; init; } = 1;

    [JsonPropertyName("ac")]
    public int Ac { get; init; } = 10;

    [JsonPropertyName("speed_ft")]
    public int SpeedFt { get; init; } = 30;

    [JsonPropertyName("attacks")]
    public NpcAttack[] Attacks { get; init; } = Array.Empty<NpcAttack>();

    /// <summary>Behavior id ("brigand", "wild_animal", "poi_guard"). Maps to <c>INpcBehavior</c> in Phase 5 M5.</summary>
    [JsonPropertyName("behavior")]
    public string Behavior { get; init; } = "brigand";

    /// <summary>Starts as Hostile / Neutral / Friendly. Phase 5 reads this on instantiation.</summary>
    [JsonPropertyName("default_allegiance")]
    public string DefaultAllegiance { get; init; } = "hostile";

    /// <summary>
    /// Phase 6 M5 — faction id this NPC owes allegiance to (matches
    /// FactionDef.Id). Empty for unaligned templates (wild animals,
    /// brigands). Drives M5 patrol-aggression: a non-hostile NPC with a
    /// faction flips to Hostile when the player's local standing with
    /// that faction crosses the HOSTILE threshold.
    /// </summary>
    [JsonPropertyName("faction")]
    public string Faction { get; init; } = "";

    /// <summary>Loot table id (Phase 5 ships ~5; lookup deferred to M6).</summary>
    [JsonPropertyName("loot_table")]
    public string LootTable { get; init; } = "";

    [JsonPropertyName("xp_award")]
    public int XpAward { get; init; } = 0;
}

public sealed record NpcAttack
{
    [JsonPropertyName("name")]
    public string Name { get; init; } = "";

    [JsonPropertyName("to_hit")]
    public int ToHit { get; init; } = 0;

    /// <summary>Damage dice expression (e.g. "1d6+2", "2d8").</summary>
    [JsonPropertyName("damage")]
    public string Damage { get; init; } = "";

    [JsonPropertyName("damage_type")]
    public string DamageType { get; init; } = "bludgeoning";

    [JsonPropertyName("reach_tiles")]
    public int ReachTiles { get; init; } = 1;

    [JsonPropertyName("range_short_tiles")]
    public int RangeShortTiles { get; init; } = 0;

    [JsonPropertyName("range_long_tiles")]
    public int RangeLongTiles { get; init; } = 0;
}

/// <summary>
/// Top-level wrapper for npc_templates.json: the template list plus the
/// per-spawnkind, per-zone template-id lookup table.
/// </summary>
public sealed record NpcTemplateContent
{
    [JsonPropertyName("templates")]
    public NpcTemplateDef[] Templates { get; init; } = Array.Empty<NpcTemplateDef>();

    /// <summary>
    /// SpawnKind name (e.g. "Brigand") → array of template ids indexed by
    /// DangerZone (0..4). Length should equal <c>C.DANGER_ZONE_MAX + 1</c>.
    /// </summary>
    [JsonPropertyName("spawn_kind_to_template_by_zone")]
    public Dictionary<string, string[]> SpawnKindToTemplateByZone { get; init; } = new();

    /// <summary>
    /// Phase 7 M2 — per-dungeon-type override. Resolves a
    /// <c>(PoiType, SpawnKind)</c> pair to a single template id, used by
    /// <see cref="Theriapolis.Core.Dungeons.DungeonPopulator"/> when filling
    /// in-room encounter slots. Per Phase 7 plan §10 open-decision #6:
    /// DungeonType supersedes DangerZone entirely once the player is inside
    /// a dungeon, so this map's value is a single template id (no zone
    /// indexing). Outer key matches the <see cref="World.PoiType"/> enum
    /// name (e.g. <c>"ImperiumRuin"</c>); inner key is the spawn-kind name
    /// (e.g. <c>"PoiGuard"</c> / <c>"WildAnimal"</c> / <c>"Brigand"</c> /
    /// <c>"Boss"</c>). Missing keys fall back to
    /// <see cref="SpawnKindToTemplateByZone"/> at <c>DangerZone</c>=2 mid.
    /// </summary>
    [JsonPropertyName("spawn_kind_to_template_by_dungeon_type")]
    public Dictionary<string, Dictionary<string, string>> SpawnKindToTemplateByDungeonType { get; init; } = new();
}
