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

namespace TakeoutSaaS.Application.App.Coupons.Handlers;

/// <summary>
/// 优惠券模板列表查询处理器。
/// </summary>
public sealed class GetCouponTemplateListQueryHandler(
    ICouponRepository couponRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<GetCouponTemplateListQuery, CouponTemplateListResultDto>
{
    /// <inheritdoc />
    public async Task<CouponTemplateListResultDto> Handle(GetCouponTemplateListQuery request, CancellationToken cancellationToken)
    {
        var page = Math.Max(1, request.Page);
        var pageSize = Math.Clamp(request.PageSize, 1, 200);

        if (!CouponTemplateMapping.TryNormalizeDisplayStatusFilter(request.Status, out var normalizedStatus))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "status 参数不合法");
        }

        CouponType? couponTypeFilter = null;
        if (!string.IsNullOrWhiteSpace(request.CouponType))
        {
            if (!CouponTemplateMapping.TryParseCouponType(request.CouponType, out var parsedType))
            {
                throw new BusinessException(ErrorCodes.BadRequest, "couponType 参数不合法");
            }

            couponTypeFilter = parsedType;
        }

        var tenantId = tenantProvider.GetCurrentTenantId();
        var templates = await couponRepository.GetTemplatesAsync(tenantId, cancellationToken);
        if (templates.Count == 0)
        {
            return new CouponTemplateListResultDto
            {
                Items = [],
                TotalCount = 0,
                Page = page,
                PageSize = pageSize,
                Stats = CouponTemplateDtoFactory.ToStatsDto(0, 0, 0, 0)
            };
        }

        var nowUtc = DateTime.UtcNow;
        var visibleTemplates = new List<ResolvedTemplateItem>(templates.Count);
        foreach (var template in templates)
        {
            var scope = CouponTemplateMapping.DeserializeStoreScope(template.StoreScopeJson, template.Id);
            if (!scope.StoreIds.Contains(request.StoreId) && !string.Equals(scope.Mode, "all", StringComparison.Ordinal))
            {
                continue;
            }

            var channels = CouponTemplateMapping.DeserializeChannels(template.ChannelsJson, template.Id);
            visibleTemplates.Add(new ResolvedTemplateItem(
                template,
                scope,
                channels,
                CouponTemplateMapping.ResolveDisplayStatus(template, nowUtc)));
        }

        if (visibleTemplates.Count == 0)
        {
            return new CouponTemplateListResultDto
            {
                Items = [],
                TotalCount = 0,
                Page = page,
                PageSize = pageSize,
                Stats = CouponTemplateDtoFactory.ToStatsDto(0, 0, 0, 0)
            };
        }

        var templateIds = visibleTemplates.Select(item => item.Template.Id).ToArray();
        var redeemedMap = await couponRepository.CountRedeemedCouponsByTemplateIdsAsync(tenantId, templateIds, cancellationToken);

        var stats = CouponTemplateDtoFactory.ToStatsDto(
            visibleTemplates.Count,
            visibleTemplates.Count(item => string.Equals(item.DisplayStatus, "ongoing", StringComparison.Ordinal)),
            visibleTemplates.Sum(item => item.Template.ClaimedQuantity),
            visibleTemplates.Sum(item => redeemedMap.GetValueOrDefault(item.Template.Id)));

        IEnumerable<ResolvedTemplateItem> filtered = visibleTemplates;
        var normalizedKeyword = request.Keyword?.Trim().ToLowerInvariant();
        if (!string.IsNullOrWhiteSpace(normalizedKeyword))
        {
            filtered = filtered.Where(item => item.Template.Name.ToLowerInvariant().Contains(normalizedKeyword));
        }

        if (couponTypeFilter.HasValue)
        {
            filtered = filtered.Where(item => item.Template.CouponType == couponTypeFilter.Value);
        }

        if (!string.IsNullOrWhiteSpace(normalizedStatus))
        {
            filtered = filtered.Where(item => string.Equals(item.DisplayStatus, normalizedStatus, StringComparison.Ordinal));
        }

        var ordered = filtered
            .OrderByDescending(item => item.Template.UpdatedAt ?? item.Template.CreatedAt)
            .ThenByDescending(item => item.Template.Id)
            .ToList();

        var total = ordered.Count;
        var paged = ordered
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(item => CouponTemplateDtoFactory.ToListItemDto(
                item.Template,
                item.Scope,
                item.Channels,
                redeemedMap.GetValueOrDefault(item.Template.Id),
                nowUtc))
            .ToList();

        return new CouponTemplateListResultDto
        {
            Items = paged,
            TotalCount = total,
            Page = page,
            PageSize = pageSize,
            Stats = stats
        };
    }

    private sealed record ResolvedTemplateItem(
        CouponTemplate Template,
        CouponStoreScopeModel Scope,
        IReadOnlyList<string> Channels,
        string DisplayStatus);
}
