using System.Text.Json.Serialization;

namespace Theriapolis.Core.Data;

/// <summary>
/// Phase 7 M0 — a single hand-authored dungeon room. Loaded from
/// <c>Content/Data/room_templates/&lt;type&gt;/*.json</c>.
///
/// A template describes:
///   - The room's footprint in tactical tiles (perimeter walls included).
///   - The 2D ASCII <see cref="Grid"/>: one char per tactical tile.
///     Legend (per Phase 7 plan §5.1):
///       <c>#</c> wall, <c>.</c> floor, <c>,</c> rubble, <c>D</c> door slot,
///       <c>@</c> encounter slot, <c>C</c> container slot, <c>T</c> trap slot,
///       <c>P</c> pillar, <c>B</c> brazier, <c>M</c> mosaic (narrative),
///       <c>S</c> stairs (entry/exit only).
///   - Door positions on the perimeter (one entry per <c>D</c> in the grid).
///   - Encounter / container / trap slot positions (which the dungeon
///     populator fills with NPCs and loot).
///   - Optional narrative text surfaced by Scent Literacy / room-clear coda.
///   - The clade that <see cref="BuiltBy"/> the room — drives the
///     clade-responsive movement multiplier (Phase 7 plan §5.4).
///
/// Templates are designer-friendly to author: edit ASCII art + a couple
/// of metadata blocks. <see cref="ContentLoader.LoadRoomTemplates"/>
/// validates grid dimensions vs declared footprint, perimeter walls,
/// and that every <c>D</c>/<c>@</c>/<c>C</c>/<c>T</c> in the grid has
/// a matching slot record.
/// </summary>
public sealed record RoomTemplateDef
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

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

    /// <summary>
    /// Dungeon type the template belongs to: <c>imperium</c>, <c>mine</c>,
    /// <c>cult</c>, <c>cave</c>, <c>overgrown</c>. Layout matchers filter
    /// templates by type when assembling a dungeon.
    /// </summary>
    [JsonPropertyName("type")]
    public string Type { get; init; } = "";

    /// <summary>
    /// Clade that built or originally inhabited the room. Drives Phase 7
    /// clade-responsive movement (a Large PC in a Mustelid tunnel takes 2×
    /// movement points). Allowed: <c>canid</c>, <c>felid</c>, <c>mustelid</c>,
    /// <c>ursid</c>, <c>cervid</c>, <c>bovid</c>, <c>leporid</c>,
    /// <c>imperium</c>, <c>none</c>. Phase-7 Imperium templates use
    /// <c>imperium</c>; templates that would be at home in any dungeon use
    /// <c>none</c>.
    /// </summary>
    [JsonPropertyName("built_by")]
    public string BuiltBy { get; init; } = "none";

    /// <summary>
    /// Size class — <c>small</c>, <c>medium</c>, <c>large</c>. Used by
    /// the layout matcher to pick room mixes appropriate to the dungeon's
    /// size band (small dungeons prefer small rooms, etc.).
    /// </summary>
    [JsonPropertyName("size_class")]
    public string SizeClass { get; init; } = "medium";

    /// <summary>
    /// Roles this template is eligible for: <c>entry</c>, <c>transit</c>,
    /// <c>narrative</c>, <c>loot</c>, <c>boss</c>, <c>dead-end</c>. A
    /// template can be eligible for multiple roles (a "pillar room" can
    /// serve as transit OR as a loot stash).
    /// </summary>
    [JsonPropertyName("roles_eligible")]
    public string[] RolesEligible { get; init; } = Array.Empty<string>();

    /// <summary>Footprint width in tactical tiles. Includes perimeter walls. Must equal Grid[*].Length.</summary>
    [JsonPropertyName("footprint_w_tiles")]
    public int FootprintWTiles { get; init; } = 1;

    /// <summary>Footprint height in tactical tiles. Includes perimeter walls. Must equal Grid.Length.</summary>
    [JsonPropertyName("footprint_h_tiles")]
    public int FootprintHTiles { get; init; } = 1;

    /// <summary>
    /// 2D ASCII art: one entry per row, one char per tactical tile.
    /// Validated for perimeter wall completeness and slot-coordinate
    /// matches at content-load time.
    /// </summary>
    [JsonPropertyName("grid")]
    public string[] Grid { get; init; } = Array.Empty<string>();

    /// <summary>Door positions in template-local coords (matches <c>D</c> chars in <see cref="Grid"/>).</summary>
    [JsonPropertyName("doors")]
    public RoomDoor[] Doors { get; init; } = Array.Empty<RoomDoor>();

    /// <summary>Encounter slot positions (matches <c>@</c> chars).</summary>
    [JsonPropertyName("encounter_slots")]
    public RoomEncounterSlot[] EncounterSlots { get; init; } = Array.Empty<RoomEncounterSlot>();

    /// <summary>Container slot positions (matches <c>C</c> chars).</summary>
    [JsonPropertyName("container_slots")]
    public RoomContainerSlot[] ContainerSlots { get; init; } = Array.Empty<RoomContainerSlot>();

    /// <summary>Trap slot positions (matches <c>T</c> chars). Phase 7 ships only tripwire traps.</summary>
    [JsonPropertyName("trap_slots")]
    public RoomTrapSlot[] TrapSlots { get; init; } = Array.Empty<RoomTrapSlot>();

    /// <summary>Decoration placements for non-slot decos (P pillar, B brazier, M mosaic).</summary>
    [JsonPropertyName("decos")]
    public RoomDecoPlacement[] Decos { get; init; } = Array.Empty<RoomDecoPlacement>();

    /// <summary>
    /// Environmental-story prose surfaced by Scent Literacy (Phase 6.5 M1)
    /// in the InteractionScreen scent-overlay panel and by the dungeon-
    /// clear coda. Null/empty for non-narrative templates.
    /// </summary>
    [JsonPropertyName("narrative_text")]
    public string? NarrativeText { get; init; } = null;

    /// <summary>Selection weight in layout assembly. Default 1.0.</summary>
    [JsonPropertyName("weight")]
    public float Weight { get; init; } = 1f;
}

public sealed record RoomDoor
{
    [JsonPropertyName("x")]
    public int X { get; init; }

    [JsonPropertyName("y")]
    public int Y { get; init; }

    /// <summary>Compass facing: "N" / "E" / "S" / "W". Door always sits on a perimeter cell.</summary>
    [JsonPropertyName("facing")]
    public string Facing { get; init; } = "S";

    /// <summary>
    /// Optional lock difficulty for this door. Empty = unlocked. Allowed:
    /// <c>trivial</c>, <c>easy</c>, <c>medium</c>, <c>hard</c> — mapped to
    /// <c>LOCK_DC_*</c> constants in code.
    /// </summary>
    [JsonPropertyName("lock")]
    public string Lock { get; init; } = "";
}

public sealed record RoomEncounterSlot
{
    [JsonPropertyName("x")]
    public int X { get; init; }

    [JsonPropertyName("y")]
    public int Y { get; init; }

    /// <summary>
    /// Spawn kind: <c>PoiGuard</c> / <c>WildAnimal</c> / <c>Brigand</c> /
    /// <c>Boss</c>. Resolved against <c>npc_templates.json</c>'s
    /// per-dungeon-type spawn-kind override map at populate time.
    /// </summary>
    [JsonPropertyName("kind")]
    public string Kind { get; init; } = "PoiGuard";

    /// <summary>Likelihood the slot fires when a layout calls for variability. 1.0 = always.</summary>
    [JsonPropertyName("weight")]
    public float Weight { get; init; } = 1f;
}

public sealed record RoomContainerSlot
{
    [JsonPropertyName("x")]
    public int X { get; init; }

    [JsonPropertyName("y")]
    public int Y { get; init; }

    /// <summary>
    /// Loot-table band: <c>t1</c> / <c>t2</c> / <c>t3</c>. The dungeon's
    /// layout maps a band to a real loot-table id at populate time
    /// (<see cref="DungeonLayoutDef.LootTablePerBand"/>).
    /// </summary>
    [JsonPropertyName("loot_table_band")]
    public string LootTableBand { get; init; } = "t1";

    /// <summary>True when the container is locked (key required, or STR/DEX check).</summary>
    [JsonPropertyName("locked")]
    public bool Locked { get; init; } = false;

    /// <summary>Optional lock difficulty if <see cref="Locked"/>: trivial/easy/medium/hard.</summary>
    [JsonPropertyName("lock")]
    public string Lock { get; init; } = "";
}

public sealed record RoomTrapSlot
{
    [JsonPropertyName("x")]
    public int X { get; init; }

    [JsonPropertyName("y")]
    public int Y { get; init; }

    /// <summary>Trap kind. Phase 7 ships only <c>tripwire</c>.</summary>
    [JsonPropertyName("kind")]
    public string Kind { get; init; } = "tripwire";

    /// <summary>Disarm DC tier: <c>trivial</c>, <c>easy</c>, <c>medium</c>.</summary>
    [JsonPropertyName("disarm_dc")]
    public string DisarmDc { get; init; } = "easy";
}

public sealed record RoomDecoPlacement
{
    [JsonPropertyName("x")]
    public int X { get; init; }

    [JsonPropertyName("y")]
    public int Y { get; init; }

    /// <summary>
    /// Deco kind name. Allowed: <c>pillar</c>, <c>brazier</c>, <c>mosaic</c>,
    /// <c>imperium_statue</c>. Trap / container / door / stairs decos are
    /// declared via their respective slot collections, not here.
    /// </summary>
    [JsonPropertyName("deco")]
    public string Deco { get; init; } = "";
}
