using Theriapolis.Core.Rules.Reputation;

namespace Theriapolis.Core.Persistence;

/// <summary>
/// Phase 6 M2 — bidirectional translator between the live
/// <see cref="PlayerReputation"/> aggregate and its serializable
/// <see cref="ReputationSnapshot"/>.
/// </summary>
public static class ReputationCodec
{
    public static ReputationSnapshot Capture(PlayerReputation rep)
    {
        var snap = new ReputationSnapshot();

        // Faction standings.
        foreach (var (k, v) in rep.Factions.Standings)
            snap.FactionStandings[k] = v;

        // Personal records.
        foreach (var pd in rep.Personal.Values)
        {
            snap.Personal.Add(new PersonalDispositionSnapshot
            {
                RoleTag                = pd.RoleTag,
                Score                  = pd.Score,
                Trust                  = (byte)pd.Trust,
                Betrayed               = pd.Betrayed,
                LastInteractionSeconds = pd.LastInteractionSeconds,
                MemoryTags             = pd.Memory.ToArray(),
                Log                    = pd.Log.Select(CaptureEvent).ToArray(),
            });
        }

        // Ledger.
        foreach (var ev in rep.Ledger.Entries)
            snap.Ledger.Add(CaptureEvent(ev));

        return snap;
    }

    public static PlayerReputation Restore(ReputationSnapshot snap)
    {
        var rep = new PlayerReputation();
        foreach (var (k, v) in snap.FactionStandings)
            rep.Factions.Set(k, v);
        foreach (var p in snap.Personal)
        {
            var pd = new PersonalDisposition
            {
                RoleTag                = p.RoleTag,
                Score                  = p.Score,
                Trust                  = (TrustLevel)p.Trust,
                Betrayed               = p.Betrayed,
                LastInteractionSeconds = p.LastInteractionSeconds,
            };
            foreach (var m in p.MemoryTags) pd.Memory.Add(m);
            foreach (var ev in p.Log) pd.Log.Add(RestoreEvent(ev));
            rep.Personal[p.RoleTag] = pd;
        }
        foreach (var ev in snap.Ledger) rep.Ledger.Append(RestoreEvent(ev));
        return rep;
    }

    private static RepEventSnapshot CaptureEvent(RepEvent ev) => new()
    {
        SequenceId       = ev.SequenceId,
        Kind             = (byte)ev.Kind,
        FactionId        = ev.FactionId,
        RoleTag          = ev.RoleTag,
        Magnitude        = ev.Magnitude,
        Note             = ev.Note,
        OriginTileX      = ev.OriginTileX,
        OriginTileY      = ev.OriginTileY,
        TimestampSeconds = ev.TimestampSeconds,
    };

    private static RepEvent RestoreEvent(RepEventSnapshot s) => new()
    {
        SequenceId       = s.SequenceId,
        Kind             = (RepEventKind)s.Kind,
        FactionId        = s.FactionId,
        RoleTag          = s.RoleTag,
        Magnitude        = s.Magnitude,
        Note             = s.Note,
        OriginTileX      = s.OriginTileX,
        OriginTileY      = s.OriginTileY,
        TimestampSeconds = s.TimestampSeconds,
    };
}
