using System.Text.Json.Serialization;

namespace Theriapolis.Core.Data;

/// <summary>
/// Phase 6 M3 — JSON-loaded dialogue tree. Each tree is a directed graph
/// of nodes; the runner walks from <see cref="Root"/> per option choice.
/// Nodes are addressed by a string id local to the tree.
///
/// Author convention: keep one tree per file in
/// <c>Content/Data/dialogues/*.json</c>. <see cref="Id"/> matches the
/// filename (sans extension) so authors can find the file by id.
/// </summary>
public sealed record DialogueDef
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    /// <summary>Starting node id when the dialogue opens.</summary>
    [JsonPropertyName("root")]
    public string Root { get; init; } = "";

    [JsonPropertyName("nodes")]
    public DialogueNodeDef[] Nodes { get; init; } = System.Array.Empty<DialogueNodeDef>();
}

public sealed record DialogueNodeDef
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    /// <summary>"npc" / "pc" / "narration".</summary>
    [JsonPropertyName("speaker")]
    public string Speaker { get; init; } = "npc";

    /// <summary>Display text. Supports placeholders {pc.name}, {npc.role}, {disposition_label}.</summary>
    [JsonPropertyName("text")]
    public string Text { get; init; } = "";

    /// <summary>Effects applied automatically when the runner enters this node.</summary>
    [JsonPropertyName("on_enter")]
    public DialogueEffectDef[] OnEnter { get; init; } = System.Array.Empty<DialogueEffectDef>();

    [JsonPropertyName("options")]
    public DialogueOptionDef[] Options { get; init; } = System.Array.Empty<DialogueOptionDef>();
}

public sealed record DialogueOptionDef
{
    [JsonPropertyName("text")]
    public string Text { get; init; } = "";

    /// <summary>Visibility predicates. Option is hidden if any condition fails.</summary>
    [JsonPropertyName("conditions")]
    public DialogueConditionDef[] Conditions { get; init; } = System.Array.Empty<DialogueConditionDef>();

    /// <summary>
    /// When set, selecting this option rolls the named skill against
    /// <see cref="DialogueSkillCheckDef.Dc"/>. The runner branches into
    /// <see cref="EffectsOnSuccess"/>+<see cref="NextOnSuccess"/> on success
    /// or <see cref="EffectsOnFailure"/>+<see cref="NextOnFailure"/> on
    /// failure. <see cref="Effects"/> and <see cref="Next"/> are ignored
    /// when a skill check is present.
    /// </summary>
    [JsonPropertyName("skill_check")]
    public DialogueSkillCheckDef? SkillCheck { get; init; }

    /// <summary>Node id to jump to when this option is selected. Empty / "&lt;end&gt;" closes the dialogue.</summary>
    [JsonPropertyName("next")]
    public string Next { get; init; } = "";

    [JsonPropertyName("effects")]
    public DialogueEffectDef[] Effects { get; init; } = System.Array.Empty<DialogueEffectDef>();

    [JsonPropertyName("next_on_success")]
    public string NextOnSuccess { get; init; } = "";

    [JsonPropertyName("next_on_failure")]
    public string NextOnFailure { get; init; } = "";

    [JsonPropertyName("effects_on_success")]
    public DialogueEffectDef[] EffectsOnSuccess { get; init; } = System.Array.Empty<DialogueEffectDef>();

    [JsonPropertyName("effects_on_failure")]
    public DialogueEffectDef[] EffectsOnFailure { get; init; } = System.Array.Empty<DialogueEffectDef>();
}

public sealed record DialogueSkillCheckDef
{
    /// <summary>Skill id (snake_case, matches SkillId.FromJson — e.g. "intimidation", "persuasion").</summary>
    [JsonPropertyName("skill")]
    public string Skill { get; init; } = "";

    [JsonPropertyName("dc")]
    public int Dc { get; init; }
}

/// <summary>Visibility predicate evaluated when the option is offered.</summary>
public sealed record DialogueConditionDef
{
    /// <summary>
    /// One of: "rep_at_least", "rep_below", "has_item", "not_has_item",
    /// "has_flag", "not_has_flag", "ability_min".
    /// </summary>
    [JsonPropertyName("kind")]
    public string Kind { get; init; } = "";

    /// <summary>Faction id (rep_at_least / rep_below). Empty = effective disposition vs current NPC.</summary>
    [JsonPropertyName("faction")]
    public string Faction { get; init; } = "";

    /// <summary>Item id (has_item / not_has_item).</summary>
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    /// <summary>Flag id (has_flag / not_has_flag).</summary>
    [JsonPropertyName("flag")]
    public string Flag { get; init; } = "";

    /// <summary>Ability id (ability_min): "STR" / "DEX" / "CON" / "INT" / "WIS" / "CHA".</summary>
    [JsonPropertyName("ability")]
    public string Ability { get; init; } = "";

    /// <summary>Numeric threshold for rep / ability. Inclusive lower bound for *_at_least and *_min.</summary>
    [JsonPropertyName("value")]
    public int Value { get; init; }
}

/// <summary>Side effect applied on option selection (or on node enter).</summary>
public sealed record DialogueEffectDef
{
    /// <summary>
    /// One of: "set_flag", "clear_flag", "give_item", "take_item",
    /// "rep_event", "open_shop", "start_quest", "give_xp".
    /// </summary>
    [JsonPropertyName("kind")]
    public string Kind { get; init; } = "";

    /// <summary>Flag id for set_flag/clear_flag.</summary>
    [JsonPropertyName("flag")]
    public string Flag { get; init; } = "";

    /// <summary>Integer value for set_flag (defaults to 1).</summary>
    [JsonPropertyName("value")]
    public int Value { get; init; } = 1;

    /// <summary>Item id for give_item/take_item.</summary>
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    /// <summary>Quantity for give_item/take_item (defaults to 1).</summary>
    [JsonPropertyName("qty")]
    public int Qty { get; init; } = 1;

    /// <summary>RepEvent payload for rep_event.</summary>
    [JsonPropertyName("event")]
    public DialogueRepEventDef? Event { get; init; }

    /// <summary>Quest id for start_quest. Phase 6 M3 ignores; M4 wires the quest engine.</summary>
    [JsonPropertyName("quest")]
    public string Quest { get; init; } = "";

    /// <summary>XP magnitude for give_xp.</summary>
    [JsonPropertyName("xp")]
    public int Xp { get; init; }
}

public sealed record DialogueRepEventDef
{
    /// <summary>RepEventKind name: "Dialogue" / "Quest" / "Combat" / "Rescue" / "Betrayal" / "Gift" / "Trade" / "Aid" / "Crime" / "Misc".</summary>
    [JsonPropertyName("kind")]
    public string Kind { get; init; } = "Dialogue";

    [JsonPropertyName("magnitude")]
    public int Magnitude { get; init; }

    [JsonPropertyName("faction")]
    public string Faction { get; init; } = "";

    /// <summary>If empty, defaults to the current NPC's role tag.</summary>
    [JsonPropertyName("role_tag")]
    public string RoleTag { get; init; } = "";

    [JsonPropertyName("note")]
    public string Note { get; init; } = "";
}
