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

namespace TakeoutSaaS.Application.App.Personal.Handlers;

/// <summary>
/// 分页查询个人中心支付记录处理器。
/// </summary>
public sealed class SearchPersonalPaymentsQueryHandler(
    PersonalContextService personalContextService,
    PersonalDateRangeValidator personalDateRangeValidator,
    PersonalAuditService personalAuditService,
    ITenantPaymentRepository tenantPaymentRepository,
    ITenantVisibilityRoleRuleRepository visibilityRoleRuleRepository)
    : IRequestHandler<SearchPersonalPaymentsQuery, PagedResult<PersonalPaymentRecordDto>>
{
    private static readonly string[] DefaultBillingVisibleRoles = ["tenant-owner", "tenant-admin"];

    /// <summary>
    /// 处理查询。
    /// </summary>
    /// <param name="request">查询请求。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>支付记录分页结果。</returns>
    public async Task<PagedResult<PersonalPaymentRecordDto>> Handle(SearchPersonalPaymentsQuery request, CancellationToken cancellationToken)
    {
        try
        {
            // 1. 获取上下文并校验角色可见性
            var context = personalContextService.GetRequiredContext();
            var rule = await visibilityRoleRuleRepository.FindByTenantIdAsync(context.TenantId, cancellationToken);
            var allowedRoles = rule?.BillingVisibleRoleCodes?.Length > 0
                ? rule.BillingVisibleRoleCodes
                : DefaultBillingVisibleRoles;
            if (!HasAnyRole(context.RoleCodes, allowedRoles))
            {
                throw new BusinessException(ErrorCodes.Forbidden, "当前角色无权访问支付数据");
            }

            // 2. 解析时间窗和分页参数
            var dateRange = personalDateRangeValidator.Resolve(request.From, request.To);
            var page = request.Page <= 0 ? 1 : request.Page;
            var pageSize = request.PageSize <= 0 ? 20 : Math.Min(request.PageSize, 50);

            // 3. 查询分页数据
            var paged = await tenantPaymentRepository.SearchPagedAsync(
                context.TenantId,
                dateRange.From,
                dateRange.To,
                page,
                pageSize,
                cancellationToken);
            var items = paged.Items.Select(MapPayment).ToList();

            // 4. 记录审计并返回
            await personalAuditService.RecordSensitiveQueryAsync("billing-payments", true, "查询支付记录成功", cancellationToken);
            return new PagedResult<PersonalPaymentRecordDto>(items, page, pageSize, paged.Total);
        }
        catch (Exception ex)
        {
            // 5. 记录失败审计并继续抛出
            await personalAuditService.RecordSensitiveQueryAsync("billing-payments", false, ex.Message, cancellationToken);
            throw;
        }
    }

    private static PersonalPaymentRecordDto MapPayment(Domain.Tenants.Entities.TenantPayment payment)
    {
        // 1. 映射支付方式与状态
        var method = payment.Method switch
        {
            TenantPaymentMethod.Online => "online",
            TenantPaymentMethod.BankTransfer => "bank_transfer",
            TenantPaymentMethod.Other => "other",
            _ => payment.Method.ToString().ToLowerInvariant()
        };
        var status = payment.Status switch
        {
            TenantPaymentStatus.Pending => "pending",
            TenantPaymentStatus.Success => "success",
            TenantPaymentStatus.Failed => "failed",
            TenantPaymentStatus.Refunded => "refunded",
            _ => payment.Status.ToString().ToLowerInvariant()
        };

        // 2. 返回 DTO
        return new PersonalPaymentRecordDto
        {
            PaymentId = payment.Id,
            StatementId = payment.BillingStatementId,
            PaidAmount = payment.Amount,
            PaymentMethod = method,
            PaymentStatus = status,
            PaidAt = payment.PaidAt
        };
    }

    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);
    }
}
