using NeonSprawl.Server.Game.PositionState;

namespace NeonSprawl.Server.Game.Mastery;

/// <summary>
/// Maps <c>GET</c>/<c>POST /game/players/{{id}}/perk-state</c> — read snapshot and branch selection (NEO-48).
/// </summary>
public static class PerkStateApi
{
    public static WebApplication MapPerkStateApi(this WebApplication app)
    {
        app.MapGet(
            "/game/players/{id}/perk-state",
            (string id, IPositionStateStore positions, IPlayerPerkStateStore perkStore) =>
            {
                var trimmedId = id.Trim();
                if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
                {
                    return Results.NotFound();
                }

                return Results.Json(BuildSnapshot(trimmedId, perkStore));
            });

        app.MapPost(
            "/game/players/{id}/perk-state",
            (string id, PerkBranchSelectRequest? body, IPositionStateStore positions, PerkUnlockEngine perkUnlockEngine) =>
            {
                if (body is null || body.SchemaVersion != PerkBranchSelectRequest.CurrentSchemaVersion)
                {
                    return Results.BadRequest();
                }

                var trimmedId = id.Trim();
                if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
                {
                    return Results.NotFound();
                }

                var outcome = perkUnlockEngine.TrySelectBranch(
                    trimmedId,
                    body.SkillId,
                    body.TierIndex,
                    body.BranchId);

                var perkState = BuildSnapshot(trimmedId, outcome.Snapshot);
                if (!outcome.Success)
                {
                    return Results.Json(
                        new PerkBranchSelectResponse
                        {
                            Selected = false,
                            ReasonCode = outcome.ReasonCode,
                            PerkState = perkState,
                            UnlockedEvents = [],
                        });
                }

                return Results.Json(
                    new PerkBranchSelectResponse
                    {
                        Selected = true,
                        PerkState = perkState,
                        UnlockedEvents = MapEvents(outcome.Events),
                    });
            });

        return app;
    }

    internal static PerkStateSnapshotResponse BuildSnapshot(string playerId, IPlayerPerkStateStore store) =>
        BuildSnapshot(playerId, store.GetSnapshot(playerId));

    internal static PerkStateSnapshotResponse BuildSnapshot(string playerId, PerkStateSnapshot snapshot)
    {
        var branchPicks = new List<PerkBranchPickTrackJson>(snapshot.BranchPicksBySkillId.Count);
        foreach (var (skillId, tiers) in snapshot.BranchPicksBySkillId.OrderBy(static kv => kv.Key, StringComparer.Ordinal))
        {
            var picks = tiers
                .OrderBy(static kv => kv.Key)
                .Select(static kv => new PerkBranchPickRowJson { TierIndex = kv.Key, BranchId = kv.Value })
                .ToList();
            branchPicks.Add(new PerkBranchPickTrackJson { SkillId = skillId, Picks = picks });
        }

        var unlocked = snapshot.UnlockedPerkIds.OrderBy(static id => id, StringComparer.Ordinal).ToList();

        return new PerkStateSnapshotResponse
        {
            PlayerId = playerId,
            BranchPicks = branchPicks,
            UnlockedPerkIds = unlocked,
        };
    }

    private static IReadOnlyList<PerkUnlockedEventJson> MapEvents(IReadOnlyList<PerkUnlockEvent> events)
    {
        if (events.Count == 0)
        {
            return [];
        }

        var mapped = new List<PerkUnlockedEventJson>(events.Count);
        foreach (var e in events)
        {
            mapped.Add(
                new PerkUnlockedEventJson
                {
                    PerkId = e.PerkId,
                    SkillId = e.SkillId,
                    TierIndex = e.TierIndex,
                    BranchId = e.BranchId,
                    Source = e.Source switch
                    {
                        PerkUnlockSource.BranchPick => "branch_pick",
                        PerkUnlockSource.LevelUp => "level_up",
                        _ => throw new InvalidOperationException($"Unexpected perk unlock source: {e.Source}"),
                    },
                });
        }

        return mapped;
    }
}
