using Microsoft.Extensions.Options;
using Web.Settings;

namespace Web.Middleware;

public sealed class SiteModeMiddleware(RequestDelegate next, IOptions<SiteSettings> options)
{
    private static readonly string[] StaticAssetPrefixes =
    [
        "/css/",
        "/js/",
        "/img/",
        "/logo/",
        "/fonts/"
    ];

    public async Task InvokeAsync(HttpContext context)
    {
        var mode = ResolveMode(options.Value.Mode);
        if (mode == SiteModes.Normal)
        {
            await next(context);
            return;
        }

        var path = context.Request.Path.Value ?? "/";
        var statusPath = GetStatusPath(mode);

        if (IsAllowedPath(path, statusPath))
        {
            if (mode == SiteModes.Unavailable &&
                path.Equals("/site-unavailable.html", StringComparison.OrdinalIgnoreCase))
            {
                context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
                context.Response.Headers.RetryAfter = "3600";
            }

            await next(context);
            return;
        }

        context.Response.Redirect(statusPath);
    }

    private static string ResolveMode(string? configuredMode)
    {
        if (string.Equals(configuredMode, SiteModes.UnderConstruction, StringComparison.OrdinalIgnoreCase))
            return SiteModes.UnderConstruction;

        if (string.Equals(configuredMode, SiteModes.Unavailable, StringComparison.OrdinalIgnoreCase))
            return SiteModes.Unavailable;

        return SiteModes.Normal;
    }

    private static string GetStatusPath(string mode) =>
        mode == SiteModes.UnderConstruction
            ? "/under-construction.html"
            : "/site-unavailable.html";

    private static bool IsAllowedPath(string path, string statusPath)
    {
        if (path.Equals(statusPath, StringComparison.OrdinalIgnoreCase))
            return true;

        if (path.Equals("/favicon.ico", StringComparison.OrdinalIgnoreCase))
            return true;

        return StaticAssetPrefixes.Any(prefix =>
            path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
    }
}
