using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.PositionState;

namespace NeonSprawl.Server.Game.Skills;

/// <summary>
/// Maps <c>GET</c>/<c>POST /game/players/{{id}}/skill-progression</c> — read snapshot (NEO-37) and XP grant apply (NEO-38).
/// </summary>
public static class SkillProgressionSnapshotApi
{
    /// <seealso cref="SkillProgressionGrantResponse.ReasonCode" />
    public const string ReasonUnknownSkill = "unknown_skill";

    public const string ReasonSourceKindNotAllowed = "source_kind_not_allowed";
    public const string ReasonInvalidAmount = "invalid_amount";

    public static WebApplication MapSkillProgressionSnapshotApi(this WebApplication app)
    {
        app.MapGet(
            "/game/players/{id}/skill-progression",
            (string id, IPositionStateStore positions, ISkillDefinitionRegistry registry, IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve) =>
            {
                var trimmedId = id.Trim();
                if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
                {
                    return Results.NotFound();
                }

                return Results.Json(BuildSnapshot(trimmedId, registry, xpStore, levelCurve));
            });

        app.MapPost(
            "/game/players/{id}/skill-progression",
            (string id, SkillProgressionGrantRequest? body, IPositionStateStore positions,
                ISkillDefinitionRegistry registry, IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve,
                PerkUnlockEngine perkUnlockEngine) =>
            {
                if (body is null || body.SchemaVersion != SkillProgressionGrantRequest.CurrentSchemaVersion)
                {
                    return Results.BadRequest();
                }

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

                var outcome = SkillProgressionGrantOperations.TryApplyGrant(
                    trimmedId,
                    body.SkillId,
                    body.Amount,
                    body.SourceKind,
                    registry,
                    xpStore,
                    levelCurve,
                    perkUnlockEngine);

                return outcome.Kind switch
                {
                    SkillProgressionGrantApplyKind.Denied => Results.Json(outcome.Response!),
                    SkillProgressionGrantApplyKind.ProgressionStoreMissing => Results.NotFound(),
                    SkillProgressionGrantApplyKind.Granted => Results.Json(outcome.Response!),
                    _ => throw new InvalidOperationException($"Unexpected grant outcome: {outcome.Kind}"),
                };
            });

        return app;
    }

    internal static SkillProgressionSnapshotResponse BuildSnapshot(
        string playerId,
        ISkillDefinitionRegistry registry,
        IPlayerSkillProgressionStore store,
        ISkillLevelCurve levelCurve)
    {
        var xpBySkill = store.GetXpTotals(playerId);
        var defs = registry.GetDefinitionsInIdOrder();
        var skills = new List<SkillProgressionRowJson>(defs.Count);
        foreach (var d in defs)
        {
            xpBySkill.TryGetValue(d.Id, out var xp);
            var level = levelCurve.LevelFromTotalXp(xp);
            skills.Add(
                new SkillProgressionRowJson
                {
                    Id = d.Id,
                    Xp = xp,
                    Level = level,
                });
        }

        skills.Sort(static (a, b) => string.CompareOrdinal(a.Id, b.Id));

        return new SkillProgressionSnapshotResponse
        {
            PlayerId = playerId,
            Skills = skills,
            SchemaVersion = SkillProgressionSnapshotResponse.CurrentSchemaVersion,
        };
    }
}
