using SVSim.EmulatedEntrypoint.Constants;
using SVSim.EmulatedEntrypoint.Security;
using SVSim.EmulatedEntrypoint.Services;

namespace SVSim.EmulatedEntrypoint.Middlewares;

/// <summary>
/// Maps an incoming request's session id to a udid if both are present.
/// </summary>
public class SessionidMappingMiddleware : IMiddleware
{
    private readonly ShadowverseSessionService _shadowverseSessionService;
    private readonly ILogger<SessionidMappingMiddleware> _logger;

    public SessionidMappingMiddleware(
        ShadowverseSessionService shadowverseSessionService,
        ILogger<SessionidMappingMiddleware> logger)
    {
        _shadowverseSessionService = shadowverseSessionService;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // NOTE: the bool names below were historically inverted (hasSessionId held UDID and
        // vice versa). Variable names corrected in-place; behavior unchanged.
        bool hasUdid = context.Request.Headers.TryGetValue(NetworkConstants.UdidHeaderName, out var udid);
        bool hasSid = context.Request.Headers.TryGetValue(NetworkConstants.SessionIdHeaderName, out var sid);

        if (hasUdid && hasSid)
        {
            string? sidValue = sid.FirstOrDefault();
            string? encodedUdid = udid.FirstOrDefault();
            try
            {
                string? decoded = Encryption.Decode(encodedUdid);
                Guid parsedUdid = Guid.Parse(decoded);
                _shadowverseSessionService.StoreUdidForSessionId(sidValue, parsedUdid);
                _logger.LogDebug(
                    "Stored SID→UDID mapping for {Path} (sid={Sid}, udid={Udid}).",
                    context.Request.Path, sidValue, parsedUdid);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "Failed to decode/parse UDID header for {Path} (sid={Sid}, encodedUdidLen={EncodedUdidLen}). " +
                    "Downstream translation will fall back to Guid.Empty and almost certainly fail msgpack decrypt.",
                    context.Request.Path, sidValue, encodedUdid?.Length ?? 0);
            }
        }
        else if (hasUdid ^ hasSid)
        {
            // Only one of the two headers present — usually a client bug or a test that forgot
            // to set both. The translation middleware will then fall back to Guid.Empty and
            // surface as a generic msgpack/decrypt error, so warn here where the cause is clear.
            _logger.LogWarning(
                "Only one of UDID/SID headers present for {Path} (hasUdid={HasUdid}, hasSid={HasSid}). " +
                "Translation will use Guid.Empty as the encryption key.",
                context.Request.Path, hasUdid, hasSid);
        }

        await next.Invoke(context);
    }
}
