using MediatR;
using TakeoutSaaS.Application.App.Personal.Dto;
using TakeoutSaaS.Application.App.Personal.Queries;
using TakeoutSaaS.Application.App.Personal.Services;
using TakeoutSaaS.Domain.Tenants.Entities;
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 UpdatePersonalVisibilityRoleConfigCommandHandler(
    PersonalContextService personalContextService,
    ITenantVisibilityRoleRuleRepository visibilityRoleRuleRepository)
    : IRequestHandler<UpdatePersonalVisibilityRoleConfigCommand, PersonalVisibilityRoleConfigDto>
{
    private static readonly string[] ManagerRoleCodes = ["tenant-owner", "tenant-admin"];

    /// <summary>
    /// 处理命令。
    /// </summary>
    /// <param name="request">更新命令。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>更新后的配置。</returns>
    public async Task<PersonalVisibilityRoleConfigDto> Handle(UpdatePersonalVisibilityRoleConfigCommand request, CancellationToken cancellationToken)
    {
        // 1. 获取上下文并校验管理权限
        var context = personalContextService.GetRequiredContext();
        if (!HasAnyRole(context.RoleCodes, ManagerRoleCodes))
        {
            throw new BusinessException(ErrorCodes.Forbidden, "当前角色无权更新可见角色配置");
        }

        // 2. 规范化角色编码集合
        var normalizedQuotaRoles = NormalizeRoleCodes(request.QuotaVisibleRoleCodes);
        var normalizedBillingRoles = NormalizeRoleCodes(request.BillingVisibleRoleCodes);

        // 3. 查询并落库
        var existing = await visibilityRoleRuleRepository.FindByTenantIdAsync(context.TenantId, cancellationToken);
        if (existing is null)
        {
            var created = new TenantVisibilityRoleRule
            {
                TenantId = context.TenantId,
                QuotaVisibleRoleCodes = normalizedQuotaRoles,
                BillingVisibleRoleCodes = normalizedBillingRoles,
                UpdatedBy = context.UserId,
                UpdatedAt = DateTime.UtcNow
            };
            await visibilityRoleRuleRepository.AddAsync(created, cancellationToken);
            await visibilityRoleRuleRepository.SaveChangesAsync(cancellationToken);

            return new PersonalVisibilityRoleConfigDto
            {
                TenantId = created.TenantId,
                QuotaVisibleRoleCodes = created.QuotaVisibleRoleCodes,
                BillingVisibleRoleCodes = created.BillingVisibleRoleCodes,
                UpdatedBy = created.UpdatedBy ?? context.UserId,
                UpdatedAt = created.UpdatedAt ?? DateTime.UtcNow
            };
        }

        existing.QuotaVisibleRoleCodes = normalizedQuotaRoles;
        existing.BillingVisibleRoleCodes = normalizedBillingRoles;
        existing.UpdatedBy = context.UserId;
        existing.UpdatedAt = DateTime.UtcNow;
        await visibilityRoleRuleRepository.UpdateAsync(existing, cancellationToken);
        await visibilityRoleRuleRepository.SaveChangesAsync(cancellationToken);

        // 4. 返回更新结果
        return new PersonalVisibilityRoleConfigDto
        {
            TenantId = existing.TenantId,
            QuotaVisibleRoleCodes = existing.QuotaVisibleRoleCodes,
            BillingVisibleRoleCodes = existing.BillingVisibleRoleCodes,
            UpdatedBy = existing.UpdatedBy ?? context.UserId,
            UpdatedAt = existing.UpdatedAt ?? DateTime.UtcNow
        };
    }

    private static string[] NormalizeRoleCodes(IEnumerable<string> roleCodes)
    {
        // 1. 过滤空值并去重
        return roleCodes
            .Where(static x => !string.IsNullOrWhiteSpace(x))
            .Select(static x => x.Trim())
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .ToArray();
    }

    private static bool HasAnyRole(IReadOnlyList<string> currentRoles, IReadOnlyList<string> allowedRoles)
    {
        // 1. 构建当前角色集合
        var roleSet = currentRoles
            .Where(static x => !string.IsNullOrWhiteSpace(x))
            .Select(static x => x.Trim())
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        // 2. 判断是否命中允许角色
        return allowedRoles.Any(roleSet.Contains);
    }
}
