using System.Text.Json;
using TakeoutSaaS.Domain.Tenants.Entities;

namespace TakeoutSaaS.Application.App.Tenants.Targeting;

/// <summary>
/// 目标受众过滤器。
/// </summary>
public static class TargetTypeFilter
{
    private static readonly JsonSerializerOptions Options = new()
    {
        PropertyNameCaseInsensitive = true
    };

    /// <summary>
    /// 判断公告是否匹配当前用户上下文。
    /// </summary>
    /// <param name="announcement">公告实体。</param>
    /// <param name="context">目标上下文。</param>
    /// <returns>是否匹配。</returns>
    public static bool IsMatch(TenantAnnouncement announcement, AnnouncementTargetContext context)
    {
        if (announcement == null)
        {
            return false;
        }

        // 1. 租户端严格限制：公告必须属于当前租户
        if (announcement.TenantId != context.TenantId)
        {
            return false;
        }

        var targetType = announcement.TargetType?.Trim();
        if (string.IsNullOrWhiteSpace(targetType))
        {
            return true;
        }

        var normalized = targetType.ToUpperInvariant();
        var parsed = TryParseParameters(announcement.TargetParameters, out var payload);

        return normalized switch
        {
            "ALL" or "ALL_TENANTS" or "TENANT_ALL" => ApplyPayloadConstraints(payload, parsed, context, allowEmpty: true),
            "SPECIFIC_TENANTS" => RequireTenantMatch(payload, parsed, context)
                && ApplyPayloadConstraints(payload, parsed, context, allowEmpty: false),
            "USERS" or "SPECIFIC_USERS" or "USER_IDS" => RequireUserMatch(payload, parsed, context)
                && ApplyPayloadConstraints(payload, parsed, context, allowEmpty: false),
            "ROLES" or "ROLE" => RequireRoleMatch(payload, parsed, context)
                && ApplyPayloadConstraints(payload, parsed, context, allowEmpty: false),
            "PERMISSIONS" or "PERMISSION" => RequirePermissionMatch(payload, parsed, context)
                && ApplyPayloadConstraints(payload, parsed, context, allowEmpty: false),
            "MERCHANTS" or "MERCHANT_IDS" => RequireMerchantMatch(payload, parsed, context)
                && ApplyPayloadConstraints(payload, parsed, context, allowEmpty: false),
            _ => ApplyPayloadConstraints(payload, parsed, context, allowEmpty: false)
        };
    }

    private static bool RequireTenantMatch(TargetParametersPayload payload, bool parsed, AnnouncementTargetContext context)
        => parsed && payload.TenantIds is { Length: > 0 } && payload.TenantIds.Contains(context.TenantId);

    private static bool RequireUserMatch(TargetParametersPayload payload, bool parsed, AnnouncementTargetContext context)
        => parsed && payload.UserIds is { Length: > 0 } && context.UserId != 0 && payload.UserIds.Contains(context.UserId);

    private static bool RequireMerchantMatch(TargetParametersPayload payload, bool parsed, AnnouncementTargetContext context)
        => parsed && payload.MerchantIds is { Length: > 0 } && context.MerchantId.HasValue && payload.MerchantIds.Contains(context.MerchantId.Value);

    private static bool RequireRoleMatch(TargetParametersPayload payload, bool parsed, AnnouncementTargetContext context)
        => parsed && payload.Roles is { Length: > 0 } && Intersects(payload.Roles, context.Roles);

    private static bool RequirePermissionMatch(TargetParametersPayload payload, bool parsed, AnnouncementTargetContext context)
        => parsed && payload.Permissions is { Length: > 0 } && Intersects(payload.Permissions, context.Permissions);

    private static bool ApplyPayloadConstraints(
        TargetParametersPayload payload,
        bool parsed,
        AnnouncementTargetContext context,
        bool allowEmpty)
    {
        if (!parsed)
        {
            return false;
        }

        if (!payload.HasConstraints)
        {
            return allowEmpty;
        }

        if (payload.TenantIds is { Length: > 0 } && !payload.TenantIds.Contains(context.TenantId))
        {
            return false;
        }

        if (payload.UserIds is { Length: > 0 })
        {
            if (context.UserId == 0 || !payload.UserIds.Contains(context.UserId))
            {
                return false;
            }
        }

        if (payload.MerchantIds is { Length: > 0 })
        {
            if (!context.MerchantId.HasValue || !payload.MerchantIds.Contains(context.MerchantId.Value))
            {
                return false;
            }
        }

        if (payload.Roles is { Length: > 0 } && !Intersects(payload.Roles, context.Roles))
        {
            return false;
        }

        if (payload.Permissions is { Length: > 0 } && !Intersects(payload.Permissions, context.Permissions))
        {
            return false;
        }

        if (payload.Departments is { Length: > 0 } && !Intersects(payload.Departments, context.Departments))
        {
            return false;
        }

        return true;
    }

    private static bool TryParseParameters(string? json, out TargetParametersPayload payload)
    {
        payload = new TargetParametersPayload();

        if (string.IsNullOrWhiteSpace(json))
        {
            return true;
        }

        try
        {
            payload = JsonSerializer.Deserialize<TargetParametersPayload>(json, Options) ?? new TargetParametersPayload();
            return true;
        }
        catch (JsonException)
        {
            return false;
        }
    }

    private static bool Intersects(IEnumerable<string> left, IEnumerable<string> right)
    {
        var set = new HashSet<string>(right ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
        foreach (var value in left ?? Array.Empty<string>())
        {
            if (set.Contains(value))
            {
                return true;
            }
        }

        return false;
    }

    private sealed class TargetParametersPayload
    {
        public long[]? TenantIds { get; init; }
        public long[]? UserIds { get; init; }
        public long[]? MerchantIds { get; init; }
        public string[]? Roles { get; init; }
        public string[]? Permissions { get; init; }
        public string[]? Departments { get; init; }

        public bool HasConstraints
            => (TenantIds?.Length ?? 0) > 0
               || (UserIds?.Length ?? 0) > 0
               || (MerchantIds?.Length ?? 0) > 0
               || (Roles?.Length ?? 0) > 0
               || (Permissions?.Length ?? 0) > 0
               || (Departments?.Length ?? 0) > 0;
    }
}

/// <summary>
/// 目标受众上下文。
/// </summary>
public sealed record AnnouncementTargetContext
{
    /// <summary>
    /// 租户 ID。
    /// </summary>
    public long TenantId { get; init; }

    /// <summary>
    /// 用户 ID。
    /// </summary>
    public long UserId { get; init; }

    /// <summary>
    /// 商户 ID（可选）。
    /// </summary>
    public long? MerchantId { get; init; }

    /// <summary>
    /// 角色集合。
    /// </summary>
    public IReadOnlyCollection<string> Roles { get; init; } = Array.Empty<string>();

    /// <summary>
    /// 权限集合。
    /// </summary>
    public IReadOnlyCollection<string> Permissions { get; init; } = Array.Empty<string>();

    /// <summary>
    /// 部门集合（可选）。
    /// </summary>
    public IReadOnlyCollection<string> Departments { get; init; } = Array.Empty<string>();
}
