using System.Text.Json.Serialization;

namespace Theriapolis.Core.Data;

/// <summary>
/// Phase 6 M1 — definition of a friendly/neutral resident NPC inhabiting a
/// settlement. Two flavours, both loaded from <c>resident_templates.json</c>:
///
/// 1. **Generic** (<c>Named == false</c>) — matches by <see cref="RoleTag"/>
///    prefix. <see cref="ResidentInstantiator"/> picks the highest-weight
///    generic whose RoleTag equals the building-role tag (e.g. "innkeeper",
///    "shopkeeper", "guard").
///
/// 2. **Named** (<c>Named == true</c>) — matches by exact <see cref="RoleTag"/>
///    (e.g. "millhaven.innkeeper"). Used when a settlement preset's
///    <c>role_overrides</c> qualifies a building role with a specific
///    anchor-prefixed tag, locking that NPC to a hand-authored species,
///    name, and bias profile.
///
/// Combat stats are minimal in M1 — residents are non-combatants by
/// default. They have a token <see cref="Hp"/>/<see cref="Ac"/> in case the
/// player attacks them; engagement promotes them to a derived combatant
/// using the existing <see cref="NpcTemplateDef"/>-style stat block.
/// </summary>
public sealed record ResidentTemplateDef
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    /// <summary>
    /// The role tag this template matches. Generic templates use bare
    /// occupations ("innkeeper"); named templates use anchor-prefixed
    /// "settlement.role" ids ("millhaven.innkeeper").
    /// </summary>
    [JsonPropertyName("role_tag")]
    public string RoleTag { get; init; } = "";

    /// <summary>True when this template is hand-authored for a specific named NPC. Always wins over generic when role_tag matches.</summary>
    [JsonPropertyName("named")]
    public bool Named { get; init; } = false;

    /// <summary>Display name shown in dialogue + tooltip. Empty for generics → resolved from <see cref="RoleTag"/>.</summary>
    [JsonPropertyName("name")]
    public string Name { get; init; } = "";

    /// <summary>Clade id (e.g. "canidae"). Required for named templates; generics may leave empty to roll from settlement biome.</summary>
    [JsonPropertyName("clade")]
    public string Clade { get; init; } = "";

    /// <summary>Species id (e.g. "wolf"). Required for named; generics roll from clade.</summary>
    [JsonPropertyName("species")]
    public string Species { get; init; } = "";

    /// <summary>Bias profile id this NPC carries (matches BiasProfileDef.Id).</summary>
    [JsonPropertyName("bias_profile")]
    public string BiasProfile { get; init; } = "URBAN_PROGRESSIVE";

    /// <summary>Faction affiliation id (matches FactionDef.Id), or empty for unaffiliated.</summary>
    [JsonPropertyName("faction")]
    public string Faction { get; init; } = "";

    /// <summary>Dialogue tree id (matches dialogues/*.json id). Empty → fall back to a generic-by-role placeholder.</summary>
    [JsonPropertyName("dialogue")]
    public string Dialogue { get; init; } = "";

    /// <summary>"friendly" or "neutral". Defaults to friendly. Hostile residents go through the npc_templates path instead.</summary>
    [JsonPropertyName("default_allegiance")]
    public string DefaultAllegiance { get; init; } = "friendly";

    /// <summary>Stat block for combat fallback. Defaults are commoner-ish (HP 8, AC 10).</summary>
    [JsonPropertyName("hp")]
    public int Hp { get; init; } = 8;

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

    [JsonPropertyName("ability_scores")]
    public Dictionary<string, int> AbilityScores { get; init; } = new();

    /// <summary>Selection weight when multiple generic templates match the same role tag.</summary>
    [JsonPropertyName("weight")]
    public float Weight { get; init; } = 1f;
}
