using MediatR;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.Dto;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.Queries;
using TakeoutSaaS.Domain.Coupons.Entities;
using TakeoutSaaS.Domain.Coupons.Enums;
using TakeoutSaaS.Domain.Coupons.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Coupons.NewCustomer.Handlers;

/// <summary>
/// 查询新客有礼详情处理器。
/// </summary>
public sealed class GetNewCustomerDetailQueryHandler(
    INewCustomerGiftRepository repository,
    ITenantProvider tenantProvider)
    : IRequestHandler<GetNewCustomerDetailQuery, NewCustomerDetailDto>
{
    /// <inheritdoc />
    public async Task<NewCustomerDetailDto> Handle(GetNewCustomerDetailQuery request, CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();
        var normalizedPage = Math.Max(1, request.RecordPage);
        var normalizedPageSize = Math.Clamp(request.RecordPageSize, 1, 200);

        var setting = await repository.FindSettingByStoreIdAsync(tenantId, request.StoreId, cancellationToken);
        var rules = await repository.GetCouponRulesByStoreIdAsync(tenantId, request.StoreId, cancellationToken);
        var settingsDto = BuildSettingsDto(request.StoreId, setting, rules);

        var nowUtc = DateTime.UtcNow;
        var currentMonthStart = NewCustomerMapping.StartOfMonthUtc(nowUtc);
        var nextMonthStart = currentMonthStart.AddMonths(1);
        var previousMonthStart = currentMonthStart.AddMonths(-1);

        var currentMonthNewCustomerCount = await repository.CountRegisteredCustomersAsync(
            tenantId,
            request.StoreId,
            currentMonthStart,
            nextMonthStart,
            cancellationToken);

        var previousMonthNewCustomerCount = await repository.CountRegisteredCustomersAsync(
            tenantId,
            request.StoreId,
            previousMonthStart,
            currentMonthStart,
            cancellationToken);

        var currentMonthGiftClaimedCount = await repository.CountGiftClaimedCustomersAsync(
            tenantId,
            request.StoreId,
            currentMonthStart,
            nextMonthStart,
            cancellationToken);

        var currentMonthFirstOrderedCount = await repository.CountFirstOrderedCustomersAsync(
            tenantId,
            request.StoreId,
            currentMonthStart,
            nextMonthStart,
            cancellationToken);

        var stats = new NewCustomerStatsDto
        {
            MonthlyNewCustomers = currentMonthNewCustomerCount,
            MonthlyGrowthCount = currentMonthNewCustomerCount - previousMonthNewCustomerCount,
            MonthlyGrowthRatePercent = NewCustomerMapping.ToGrowthRatePercent(
                currentMonthNewCustomerCount,
                previousMonthNewCustomerCount),
            GiftClaimRate = NewCustomerMapping.ToRatePercent(
                currentMonthGiftClaimedCount,
                currentMonthNewCustomerCount),
            GiftClaimedCount = currentMonthGiftClaimedCount,
            FirstOrderConversionRate = NewCustomerMapping.ToRatePercent(
                currentMonthFirstOrderedCount,
                currentMonthNewCustomerCount),
            FirstOrderedCount = currentMonthFirstOrderedCount
        };

        var (records, totalCount) = await repository.GetInviteRecordsAsync(
            tenantId,
            request.StoreId,
            normalizedPage,
            normalizedPageSize,
            cancellationToken);

        return new NewCustomerDetailDto
        {
            Settings = settingsDto,
            Stats = stats,
            InviteRecords = new NewCustomerInviteRecordListResultDto
            {
                Items = records.Select(NewCustomerMapping.ToInviteRecordDto).ToList(),
                Page = normalizedPage,
                PageSize = normalizedPageSize,
                TotalCount = totalCount
            }
        };
    }

    private static NewCustomerSettingsDto BuildSettingsDto(
        long storeId,
        NewCustomerGiftSetting? setting,
        IReadOnlyList<NewCustomerCouponRule> rules)
    {
        var welcomeCoupons = rules
            .Where(item => item.Scene == NewCustomerCouponScene.Welcome)
            .Select(NewCustomerMapping.ToCouponRuleDto)
            .ToList();

        var inviterCoupons = rules
            .Where(item => item.Scene == NewCustomerCouponScene.InviterReward)
            .Select(NewCustomerMapping.ToCouponRuleDto)
            .ToList();

        var inviteeCoupons = rules
            .Where(item => item.Scene == NewCustomerCouponScene.InviteeReward)
            .Select(NewCustomerMapping.ToCouponRuleDto)
            .ToList();

        if (setting is null)
        {
            return new NewCustomerSettingsDto
            {
                StoreId = storeId,
                GiftEnabled = true,
                GiftType = "coupon",
                InviteEnabled = true,
                ShareChannels = ["wechat_friend", "moments"],
                WelcomeCoupons = welcomeCoupons,
                InviterCoupons = inviterCoupons,
                InviteeCoupons = inviteeCoupons,
                UpdatedAt = DateTime.UtcNow
            };
        }

        return new NewCustomerSettingsDto
        {
            StoreId = storeId,
            GiftEnabled = setting.GiftEnabled,
            GiftType = NewCustomerMapping.ToGiftTypeText(setting.GiftType),
            DirectReduceAmount = setting.DirectReduceAmount,
            DirectMinimumSpend = setting.DirectMinimumSpend,
            InviteEnabled = setting.InviteEnabled,
            ShareChannels = NewCustomerMapping.DeserializeShareChannels(setting.ShareChannelsJson),
            WelcomeCoupons = welcomeCoupons,
            InviterCoupons = inviterCoupons,
            InviteeCoupons = inviteeCoupons,
            UpdatedAt = setting.UpdatedAt ?? setting.CreatedAt
        };
    }
}
