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

namespace TakeoutSaaS.Application.App.Customers.Handlers;

/// <summary>
/// 客户分析总览查询处理器。
/// </summary>
public sealed class GetCustomerAnalysisOverviewQueryHandler(
    IOrderRepository orderRepository,
    IDapperExecutor dapperExecutor,
    ITenantProvider tenantProvider)
    : IRequestHandler<GetCustomerAnalysisOverviewQuery, CustomerAnalysisOverviewDto>
{
    /// <inheritdoc />
    public async Task<CustomerAnalysisOverviewDto> Handle(
        GetCustomerAnalysisOverviewQuery request,
        CancellationToken cancellationToken)
    {
        var periodDays = Math.Clamp(request.PeriodDays, 7, 365);
        var periodCode = string.IsNullOrWhiteSpace(request.PeriodCode)
            ? "30d"
            : request.PeriodCode.Trim().ToLowerInvariant();

        if (request.VisibleStoreIds.Count == 0)
        {
            return BuildEmptyOverview(periodCode, periodDays);
        }

        var customers = await CustomerAnalyticsSupport.LoadCustomersAsync(
            orderRepository,
            dapperExecutor,
            tenantProvider,
            request.VisibleStoreIds,
            cancellationToken);

        var nowUtc = DateTime.UtcNow;
        var currentStart = nowUtc.AddDays(-periodDays);
        var previousStart = currentStart.AddDays(-periodDays);

        var totalCustomers = customers.Count;
        var newCustomers = customers.Count(item => item.RegisteredAt >= currentStart);
        var previousNewCustomers = customers.Count(item =>
            item.RegisteredAt >= previousStart &&
            item.RegisteredAt < currentStart);
        var activeCustomers = customers.Count(item => item.LastOrderAt >= currentStart);

        var averageLifetimeValue = totalCustomers <= 0
            ? 0
            : decimal.Round(
                customers.Sum(item => item.TotalAmount) / totalCustomers,
                2,
                MidpointRounding.AwayFromZero);

        return new CustomerAnalysisOverviewDto
        {
            PeriodCode = periodCode,
            PeriodDays = periodDays,
            TotalCustomers = totalCustomers,
            NewCustomers = newCustomers,
            GrowthRatePercent = CustomerAnalyticsSupport.ToGrowthRatePercent(newCustomers, previousNewCustomers),
            NewCustomersDailyAverage = decimal.Round(
                newCustomers / Math.Max(1m, periodDays),
                1,
                MidpointRounding.AwayFromZero),
            ActiveCustomers = activeCustomers,
            ActiveRatePercent = CustomerAnalyticsSupport.ToRatePercent(activeCustomers, totalCustomers),
            AverageLifetimeValue = averageLifetimeValue,
            GrowthTrend = CustomerAnalysisSegmentSupport.BuildGrowthTrend(customers, nowUtc, 12),
            Composition = CustomerAnalysisSegmentSupport.BuildComposition(customers, nowUtc, periodDays),
            AmountDistribution = CustomerAnalysisSegmentSupport.BuildAmountDistribution(customers),
            RfmRows = CustomerAnalysisSegmentSupport.BuildRfmRows(customers, nowUtc),
            TopCustomers = CustomerAnalysisSegmentSupport.BuildTopCustomers(customers, 10)
        };
    }

    private static CustomerAnalysisOverviewDto BuildEmptyOverview(string periodCode, int periodDays)
    {
        return new CustomerAnalysisOverviewDto
        {
            PeriodCode = periodCode,
            PeriodDays = periodDays,
            GrowthTrend = CustomerAnalysisSegmentSupport.BuildGrowthTrend([], DateTime.UtcNow, 12),
            Composition = CustomerAnalysisSegmentSupport.BuildComposition([], DateTime.UtcNow, periodDays),
            AmountDistribution = CustomerAnalysisSegmentSupport.BuildAmountDistribution([]),
            RfmRows = CustomerAnalysisSegmentSupport.BuildRfmRows([], DateTime.UtcNow),
            TopCustomers = []
        };
    }
}
