using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Module.Tenancy;

/// <summary>
/// 多租户解析中间件：支持 Header、域名与 Token Claim 的优先级解析。
/// </summary>
/// <remarks>
/// 初始化中间件。
/// </remarks>
public sealed class TenantResolutionMiddleware(
    RequestDelegate next,
    ILogger<TenantResolutionMiddleware> logger,
    ITenantContextAccessor tenantContextAccessor,
    IOptionsMonitor<TenantResolutionOptions> optionsMonitor)
{
    /// <summary>
    /// 解析租户并将上下文注入请求。
    /// </summary>
    public async Task InvokeAsync(HttpContext context, ITenantCodeResolver tenantCodeResolver)
    {
        var options = optionsMonitor.CurrentValue ?? new TenantResolutionOptions();
        if (ShouldSkip(context.Request.Path, options))
        {
            await next(context);
            return;
        }

        var tenantContext = await ResolveTenantAsync(context, options, tenantCodeResolver);
        tenantContextAccessor.Current = tenantContext;
        context.Items[TenantConstants.HttpContextItemKey] = tenantContext;

        if (!tenantContext.IsResolved)
        {
            logger.LogDebug("未能解析租户：{Path}", context.Request.Path);

            if (options.ThrowIfUnresolved)
            {
                var response = ApiResponse.Error(ErrorCodes.BadRequest, "缺少租户标识");
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                await context.Response.WriteAsJsonAsync(response, cancellationToken: context.RequestAborted);
                tenantContextAccessor.Current = null;
                context.Items.Remove(TenantConstants.HttpContextItemKey);
                return;
            }
        }

        try
        {
            await next(context);
        }
        finally
        {
            tenantContextAccessor.Current = null;
            context.Items.Remove(TenantConstants.HttpContextItemKey);
        }
    }

    private static bool ShouldSkip(PathString path, TenantResolutionOptions options)
    {
        if (!path.HasValue)
        {
            return false;
        }

        var value = path.Value ?? string.Empty;
        if (options.IgnoredPaths.Contains(value))
        {
            return true;
        }

        return options.IgnoredPaths.Any(ignore =>
        {
            if (string.IsNullOrWhiteSpace(ignore))
            {
                return false;
            }

            var ignorePath = new PathString(ignore);
            return path.StartsWithSegments(ignorePath);
        });
    }

    private static async Task<TenantContext> ResolveTenantAsync(
        HttpContext context,
        TenantResolutionOptions options,
        ITenantCodeResolver tenantCodeResolver)
    {
        var request = context.Request;
        var isAuthenticated = context.User?.Identity?.IsAuthenticated == true;

        // 1. Token Claim（已认证请求必须以 Claim 为准，避免 Header 覆盖导致跨租户访问）
        var claim = context.User?.FindFirst("tenant_id");
        if (claim != null && long.TryParse(claim.Value, out var claimTenant) && claimTenant > 0)
        {
            return new TenantContext(claimTenant, null, "claim:tenant_id");
        }
        // 1.1 (空行后) 已认证但缺少合法租户 Claim，则视为未解析（不允许 Header 覆盖）
        if (isAuthenticated)
        {
            return TenantContext.Empty;
        }

        // 2. Header 中的租户 ID
        if (!string.IsNullOrWhiteSpace(options.TenantIdHeaderName) &&
            request.Headers.TryGetValue(options.TenantIdHeaderName, out var tenantHeader) &&
            long.TryParse(tenantHeader.FirstOrDefault(), out var headerTenantId) &&
            headerTenantId > 0)
        {
            return new TenantContext(headerTenantId, null, $"header:{options.TenantIdHeaderName}");
        }

        // 3. Header 中的租户编码
        if (!string.IsNullOrWhiteSpace(options.TenantCodeHeaderName) &&
            request.Headers.TryGetValue(options.TenantCodeHeaderName, out var codeHeader))
        {
            var code = codeHeader.FirstOrDefault();
            var tenantFromCode = await ResolveByCodeAsync(code, options, tenantCodeResolver, context.RequestAborted);
            if (tenantFromCode.HasValue)
            {
                return new TenantContext(tenantFromCode.Value, code, $"header:{options.TenantCodeHeaderName}");
            }
        }

        // 4. Host 映射/子域名解析
        var host = request.Host.Host;
        if (!string.IsNullOrWhiteSpace(host))
        {
            // 4.1 精确域名映射
            if (options.DomainTenantMap.TryGetValue(host, out var tenantFromHost) && tenantFromHost > 0)
            {
                return new TenantContext(tenantFromHost, null, $"host:{host}");
            }

            // 4.2 子域名解析
            var codeFromHost = ResolveCodeFromHost(host, options.RootDomain);
            var tenantFromSubdomain = await ResolveByCodeAsync(codeFromHost, options, tenantCodeResolver, context.RequestAborted);
            if (tenantFromSubdomain.HasValue)
            {
                return new TenantContext(tenantFromSubdomain.Value, codeFromHost, $"host:{host}");
            }
        }

        return TenantContext.Empty;
    }

    private static async Task<long?> ResolveByCodeAsync(
        string? code,
        TenantResolutionOptions options,
        ITenantCodeResolver tenantCodeResolver,
        CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(code))
        {
            return null;
        }

        // 1. 优先从静态配置查找（兼容旧配置）
        if (options.CodeTenantMap.TryGetValue(code, out var tenantId) && tenantId > 0)
        {
            return tenantId;
        }

        // 2. 从数据库动态查询
        return await tenantCodeResolver.ResolveAsync(code, cancellationToken);
    }

    private static string? ResolveCodeFromHost(string host, string? rootDomain)
    {
        if (string.IsNullOrWhiteSpace(rootDomain))
        {
            return null;
        }

        var normalizedRoot = rootDomain.TrimStart('.');
        if (!host.EndsWith(normalizedRoot, StringComparison.OrdinalIgnoreCase))
        {
            return null;
        }

        var suffixLength = normalizedRoot.Length;
        if (host.Length <= suffixLength)
        {
            return null;
        }

        var withoutRoot = host[..(host.Length - suffixLength)];
        if (withoutRoot.EndsWith('.'))
        {
            withoutRoot = withoutRoot[..^1];
        }

        var segments = withoutRoot.Split('.', StringSplitOptions.RemoveEmptyEntries);
        return segments.Length == 0 ? null : segments[0];
    }
}
