using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.App.Orders.Queries;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Orders.Handlers;

/// <summary>
/// 全部订单统计查询处理器。
/// </summary>
public sealed class GetOrderAllStatsQueryHandler(
    IOrderRepository orderRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<GetOrderAllStatsQuery, OrderAllStatsDto>
{
    /// <inheritdoc />
    public async Task<OrderAllStatsDto> Handle(GetOrderAllStatsQuery request, CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();

        var orders = (await orderRepository.SearchAllOrdersAsync(
                tenantId,
                request.StoreId,
                request.StartAt,
                request.EndAt,
                request.Status,
                request.DeliveryType,
                request.PaymentMethod,
                request.Keyword,
                cancellationToken))
            .ToList();

        var refundedIds = await LoadRefundedOrderIdsAsync(orders, tenantId, cancellationToken);
        if (request.RefundedOnly)
        {
            orders = orders.Where(order => refundedIds.Contains(order.Id)).ToList();
        }

        var totalOrders = orders.Count;
        if (totalOrders == 0)
        {
            return new OrderAllStatsDto();
        }

        var totalAmount = orders.Sum(ResolveDisplayAmount);
        var averageAmount = decimal.Round(totalAmount / totalOrders, 2, MidpointRounding.AwayFromZero);
        var refundCount = request.RefundedOnly
            ? totalOrders
            : orders.Count(order => refundedIds.Contains(order.Id));

        return new OrderAllStatsDto
        {
            TotalOrders = totalOrders,
            TotalAmount = totalAmount,
            AverageAmount = averageAmount,
            RefundCount = refundCount
        };
    }

    private async Task<HashSet<long>> LoadRefundedOrderIdsAsync(
        IReadOnlyCollection<Order> orders,
        long tenantId,
        CancellationToken cancellationToken)
    {
        var orderIds = orders.Select(order => order.Id).ToList();
        if (orderIds.Count == 0)
        {
            return [];
        }

        var refunded = await orderRepository.GetRefundedOrderIdsAsync(orderIds, tenantId, cancellationToken);
        return refunded.ToHashSet();
    }

    private static decimal ResolveDisplayAmount(Order order)
    {
        return order.PaidAmount > 0 ? order.PaidAmount : order.PayableAmount;
    }
}
