using MediatR;
using TakeoutSaaS.Application.App.Personal.Dto;
using TakeoutSaaS.Application.App.Personal.Queries;
using TakeoutSaaS.Application.App.Personal.Services;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Application.App.Personal.Handlers;

/// <summary>
/// 获取个人中心配额摘要处理器。
/// </summary>
public sealed class GetPersonalQuotaQueryHandler(
    PersonalContextService personalContextService,
    PersonalAuditService personalAuditService,
    ITenantQuotaUsageRepository tenantQuotaUsageRepository,
    ITenantVisibilityRoleRuleRepository visibilityRoleRuleRepository)
    : IRequestHandler<GetPersonalQuotaQuery, PersonalQuotaUsageSummaryDto>
{
    private static readonly string[] DefaultQuotaVisibleRoles = ["tenant-owner", "tenant-admin"];

    /// <summary>
    /// 处理查询。
    /// </summary>
    /// <param name="request">查询请求。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>配额摘要。</returns>
    public async Task<PersonalQuotaUsageSummaryDto> Handle(GetPersonalQuotaQuery request, CancellationToken cancellationToken)
    {
        try
        {
            // 1. 获取请求上下文
            var context = personalContextService.GetRequiredContext();

            // 2. 校验可见角色权限
            var rule = await visibilityRoleRuleRepository.FindByTenantIdAsync(context.TenantId, cancellationToken);
            var allowedRoles = rule?.QuotaVisibleRoleCodes?.Length > 0
                ? rule.QuotaVisibleRoleCodes
                : DefaultQuotaVisibleRoles;
            if (!HasAnyRole(context.RoleCodes, allowedRoles))
            {
                throw new BusinessException(ErrorCodes.Forbidden, "当前角色无权访问配额数据");
            }

            // 3. 查询配额并组装摘要
            var usages = await tenantQuotaUsageRepository.GetByTenantAsync(context.TenantId, cancellationToken);
            var items = usages
                .OrderBy(x => x.QuotaType)
                .Select(MapQuotaItem)
                .ToList();

            // 4. 记录审计并返回
            await personalAuditService.RecordSensitiveQueryAsync("quota", true, "查询配额摘要成功", cancellationToken);
            return new PersonalQuotaUsageSummaryDto
            {
                Items = items
            };
        }
        catch (Exception ex)
        {
            // 5. 记录失败审计并继续抛出
            await personalAuditService.RecordSensitiveQueryAsync("quota", false, ex.Message, cancellationToken);
            throw;
        }
    }

    private static PersonalQuotaUsageItemDto MapQuotaItem(TakeoutSaaS.Domain.Tenants.Entities.TenantQuotaUsage usage)
    {
        // 1. 映射元数据
        var metadata = usage.QuotaType switch
        {
            TenantQuotaType.StoreCount => ("store_count", "门店数量", "个"),
            TenantQuotaType.AccountCount => ("account_count", "账号数量", "个"),
            TenantQuotaType.Storage => ("storage", "存储空间", "GB"),
            TenantQuotaType.SmsCredits => ("sms_credits", "短信额度", "条"),
            TenantQuotaType.DeliveryOrders => ("delivery_orders", "配送订单", "单"),
            TenantQuotaType.PromotionSlots => ("promotion_slots", "营销位", "个"),
            _ => (usage.QuotaType.ToString().ToLowerInvariant(), usage.QuotaType.ToString(), string.Empty)
        };

        // 2. 计算使用率
        var ratio = usage.LimitValue <= 0 ? 0 : Math.Round(usage.UsedValue / usage.LimitValue, 4);

        // 3. 返回 DTO
        return new PersonalQuotaUsageItemDto
        {
            QuotaCode = metadata.Item1,
            QuotaName = metadata.Item2,
            LimitValue = usage.LimitValue,
            UsedValue = usage.UsedValue,
            Unit = metadata.Item3,
            UsageRatio = ratio
        };
    }

    private static bool HasAnyRole(IReadOnlyList<string> currentRoles, IReadOnlyList<string> allowedRoles)
    {
        // 1. 构建角色集合并执行交集判断
        var current = currentRoles
            .Where(static x => !string.IsNullOrWhiteSpace(x))
            .Select(static x => x.Trim())
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        // 2. 返回是否命中任一允许角色
        return allowedRoles.Any(current.Contains);
    }
}
