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

namespace NeonSprawl.Server.Game.Interaction;

/// <summary>Maps <c>POST /game/players/{{id}}/interact</c> (NS-18).</summary>
public static class InteractionApi
{
    public const string ReasonOutOfRange = "out_of_range";
    public const string ReasonUnknownInteractable = "unknown_interactable";

    public static WebApplication MapInteractionApi(this WebApplication app)
    {
        app.MapPost(
            "/game/players/{id}/interact",
            (string id, InteractionRequest? body, IPositionStateStore store,
                ISkillDefinitionRegistry registry, IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve,
                PerkUnlockEngine perkUnlockEngine) =>
            {
                if (body is null || body.SchemaVersion != InteractionRequest.CurrentSchemaVersion)
                {
                    return Results.BadRequest();
                }

                var rawId = body.InteractableId;
                if (rawId is null)
                {
                    return Results.BadRequest();
                }

                var interactableKey = rawId.Trim();
                if (interactableKey.Length == 0)
                {
                    return Results.BadRequest();
                }

                var lookupKey = interactableKey.ToLowerInvariant();
                var playerKey = id.Trim();
                if (!store.TryGetPosition(playerKey, out var snap))
                {
                    return Results.NotFound();
                }

                if (!PrototypeInteractableRegistry.TryGet(lookupKey, out var entry))
                {
                    return Results.Json(
                        new InteractionResponse
                        {
                            SchemaVersion = InteractionResponse.CurrentSchemaVersion,
                            Allowed = false,
                            ReasonCode = ReasonUnknownInteractable,
                        });
                }

                if (!HorizontalReach.IsWithinHorizontalRadius(
                        snap.X,
                        snap.Z,
                        entry.X,
                        entry.Z,
                        entry.InteractionRadius))
                {
                    return Results.Json(
                        new InteractionResponse
                        {
                            SchemaVersion = InteractionResponse.CurrentSchemaVersion,
                            Allowed = false,
                            ReasonCode = ReasonOutOfRange,
                        });
                }

                if (string.Equals(entry.Kind, PrototypeInteractableRegistry.ResourceNodeKind, StringComparison.Ordinal))
                {
                    // NEO-41: prototype gather completion — same grant rules/persistence as POST …/skill-progression.
                    // Interact response stays Allowed=true; grant outcome is intentionally ignored (silent no-op on deny / store miss per plan).
                    _ = SkillProgressionGrantOperations.TryApplyGrant(
                        playerKey,
                        GatherSkillXpConstants.SalvageSkillId,
                        GatherSkillXpConstants.ActivityXpPerResourceNodeGather,
                        GatherSkillXpConstants.ActivitySourceKind,
                        registry,
                        xpStore,
                        levelCurve,
                        perkUnlockEngine);
                }

                return Results.Json(
                    new InteractionResponse
                    {
                        SchemaVersion = InteractionResponse.CurrentSchemaVersion,
                        Allowed = true,
                        InteractableId = lookupKey,
                        Payload = new InteractionPlaceholderPayload(),
                    });
            });

        return app;
    }
}
