using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc;
using Tiku.Api.Options;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;

namespace Tiku.Api.Middleware;

public sealed class TenantResolutionMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(
        HttpContext context,
        ITenantDirectory tenantDirectory,
        ITenantContextInitializer tenantInitializer,
        IOptions<TenantResolutionOptions> options)
    {
        var host = NormalizeHost(context.Request.Host.Host);
        var isPlatformHost = options.Value.PlatformHosts.Any(candidate =>
            string.Equals(NormalizeHost(candidate), host, StringComparison.OrdinalIgnoreCase));

        TenantDirectoryEntry? tenant = null;
        if (!isPlatformHost && host is not null)
        {
            tenant = await tenantDirectory.FindByHostAsync(host, context.RequestAborted);
            if (tenant is null && !IsExemptPath(context.Request.Path, options.Value.ExemptPathPrefixes))
            {
                context.Response.StatusCode = StatusCodes.Status404NotFound;
                await context.Response.WriteAsJsonAsync(new ProblemDetails
                {
                    Title = "Tenant was not found.",
                    Status = StatusCodes.Status404NotFound,
                    Detail = "The request host is not assigned to an active tenant."
                }, context.RequestAborted);
                return;
            }
        }
        else if (isPlatformHost && IsAllowedTenantCodePath(
                     context.Request.Path,
                     options.Value.TenantCodePathPrefixes))
        {
            var tenantCode = context.Request.Headers["x-tenant-code"].FirstOrDefault()
                             ?? context.Request.Query["tenantCode"].FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(tenantCode))
            {
                tenant = await tenantDirectory.FindByCodeAsync(tenantCode.Trim(), context.RequestAborted);
            }
        }

        if (tenant is not null)
        {
            tenantInitializer.Initialize(
                tenant.TenantId,
                tenant.TenantCode,
                tenant.Host is null ? TenantResolutionSource.TenantCode : TenantResolutionSource.Host);
        }

        await next(context);
    }

    private static string? NormalizeHost(string? host)
    {
        return string.IsNullOrWhiteSpace(host) ? null : host.Trim().TrimEnd('.').ToLowerInvariant();
    }

    private static bool IsExemptPath(PathString path, IEnumerable<string> prefixes)
    {
        return prefixes.Any(prefix => path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase));
    }

    private static bool IsAllowedTenantCodePath(PathString path, IEnumerable<string> prefixes)
    {
        return prefixes.Any(prefix => path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase));
    }
}
