namespace Theriapolis.Core.Rules.Reputation;

/// <summary>
/// Phase 6 M2 — Layer 3 of the disposition stack: how a *specific* NPC
/// feels about the player based on direct personal experience. Per
/// <c>reputation.md §I-3</c>, only NPCs the player has actually
/// interacted with accumulate one of these — generic shopkeepers walked
/// past don't bloat state.
///
/// Keyed by role tag (anchor-prefixed for named NPCs:
/// "millhaven.innkeeper"). Generic NPCs that the player talks to register
/// briefly under their generic tag but typically reset on chunk evict —
/// only named NPCs carry a stable id across reloads.
/// </summary>
public sealed class PersonalDisposition
{
    /// <summary>Role tag identifying which NPC this record belongs to.</summary>
    public string RoleTag { get; init; } = "";

    /// <summary>Score relative to neutral, clamped to <c>±C.REP_MAX</c>.</summary>
    public int Score { get; set; }

    /// <summary>Trust ladder accumulated across the interaction log.</summary>
    public TrustLevel Trust { get; set; } = TrustLevel.Stranger;

    /// <summary>True after the player betrayed this specific NPC. Sticky — only narrative
    /// effects can clear it.</summary>
    public bool Betrayed { get; set; }

    /// <summary>Last interaction time in WorldClock seconds. 0 = never interacted.</summary>
    public long LastInteractionSeconds { get; set; }

    /// <summary>Free-form memory tags ("saved-my-kit", "lied-about-rawfang", ...).</summary>
    public HashSet<string> Memory { get; } = new();

    /// <summary>Last N events affecting this specific NPC. Bounded — see <see cref="MaxLogEntries"/>.</summary>
    public List<RepEvent> Log { get; } = new();

    public const int MaxLogEntries = 32;

    /// <summary>Append a personal event and apply its magnitude to <see cref="Score"/>.</summary>
    public void Apply(RepEvent ev)
    {
        Score = System.Math.Clamp(Score + ev.Magnitude, C.REP_MIN, C.REP_MAX);
        if (ev.Kind == RepEventKind.Betrayal && ev.Magnitude < 0) Betrayed = true;
        Log.Add(ev);
        if (Log.Count > MaxLogEntries) Log.RemoveAt(0);
        LastInteractionSeconds = ev.TimestampSeconds;
        Trust = ComputeTrust();
    }

    /// <summary>
    /// Trust ladder derived from positive interaction count. Negative events
    /// don't promote; betrayal demotes to Stranger regardless of history.
    /// </summary>
    private TrustLevel ComputeTrust()
    {
        if (Betrayed) return TrustLevel.Stranger;
        int positives = 0;
        foreach (var e in Log) if (e.Magnitude > 0) positives++;
        return positives switch
        {
            >= 12 => TrustLevel.Bonded,
            >=  7 => TrustLevel.Trusted,
            >=  3 => TrustLevel.Familiar,
            >=  1 => TrustLevel.Acquaintance,
            _      => TrustLevel.Stranger,
        };
    }
}
