using System.Net;

namespace planli.frontend.Services;

/// <summary>
/// Reactive 401 handler.  Cookies are sent automatically by <see cref="CookieHandler"/>,
/// so this layer only needs to handle the case where the access-token has expired:
/// try a silent refresh, then retry idempotent requests (GET / DELETE) once.
/// </summary>
public class AuthHttpHandler(
    RefreshService          refreshService,
    PlanliAuthStateProvider authState) : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken ct)
    {
        // Don't intercept auth endpoints — would cause infinite recursion
        if (IsAuthEndpoint(request))
            return await base.SendAsync(request, ct);

        var response = await base.SendAsync(request, ct);

        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            var refreshed = await refreshService.TryRefreshAsync();

            if (refreshed)
            {
                // Safe to retry only requests without a consumed body
                if (request.Method == HttpMethod.Get ||
                    request.Method == HttpMethod.Delete)
                {
                    var retry = new HttpRequestMessage(request.Method, request.RequestUri);
                    foreach (var h in request.Headers)
                        retry.Headers.TryAddWithoutValidation(h.Key, h.Value);
                    return await base.SendAsync(retry, ct);
                }
                // POST/PUT: caller sees the 401; the new cookie is already set,
                // so the next request they make will succeed.
            }
            else
            {
                // Refresh failed — session is over, force logout
                authState.NotifyUserLogout();
            }
        }

        return response;
    }

    private static bool IsAuthEndpoint(HttpRequestMessage req) =>
        req.RequestUri?.PathAndQuery
           .Contains("api/Auth/", StringComparison.OrdinalIgnoreCase) == true;
}
