using MediatR;
using TakeoutSaaS.Application.App.Members.Commands;
using TakeoutSaaS.Application.App.Members.Dto;
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Membership.Repositories;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Members.Handlers;

/// <summary>
/// 保存会员等级命令处理器。
/// </summary>
public sealed class SaveMemberTierCommandHandler(
    IMemberRepository memberRepository,
    IOrderRepository orderRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<SaveMemberTierCommand, MemberTierDetailDto>
{
    /// <inheritdoc />
    public async Task<MemberTierDetailDto> Handle(
        SaveMemberTierCommand request,
        CancellationToken cancellationToken)
    {
        await MemberCenterSupport.EnsureMemberCenterInitializedAsync(
            memberRepository,
            orderRepository,
            tenantProvider,
            cancellationToken);

        var tenantId = tenantProvider.GetCurrentTenantId();
        var tiers = await memberRepository.GetTiersAsync(tenantId, cancellationToken);
        var normalizedName = (request.Name ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalizedName))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "等级名称不能为空");
        }

        if (normalizedName.Length > 64)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "等级名称不能超过 64 个字符");
        }

        var normalizedIconKey = (request.IconKey ?? "user").Trim().ToLowerInvariant();
        if (string.IsNullOrWhiteSpace(normalizedIconKey) || normalizedIconKey.Length > 32)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "等级图标不合法");
        }

        var normalizedColorHex = NormalizeColorHex(request.ColorHex);
        var normalizedRuleType = MemberCenterSupport.NormalizeRuleType(request.Rule.UpgradeRuleType);
        var amountThreshold = request.Rule.UpgradeAmountThreshold;
        var countThreshold = request.Rule.UpgradeOrderCountThreshold;

        if (normalizedRuleType is "amount" or "both")
        {
            if (!amountThreshold.HasValue || amountThreshold.Value <= 0)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "升级金额门槛必须大于 0");
            }
        }

        if (normalizedRuleType is "count" or "both")
        {
            if (!countThreshold.HasValue || countThreshold.Value <= 0)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "升级消费次数门槛必须大于 0");
            }
        }

        var normalizedDowngradeWindowDays = Math.Clamp(request.Rule.DowngradeWindowDays <= 0 ? 90 : request.Rule.DowngradeWindowDays, 30, 365);
        var normalizedSortOrder = request.SortOrder > 0
            ? request.SortOrder
            : (request.TierId.HasValue
                ? tiers.FirstOrDefault(item => item.Id == request.TierId.Value)?.SortOrder ?? 1
                : (tiers.Count == 0 ? 1 : tiers.Max(item => item.SortOrder) + 1));

        var normalizedBenefits = MemberCenterSupport.NormalizeBenefits(request.Benefits);
        var duplicate = tiers.Any(item =>
            item.Id != (request.TierId ?? 0) &&
            string.Equals(item.Name, normalizedName, StringComparison.OrdinalIgnoreCase));
        if (duplicate)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "等级名称已存在");
        }

        MemberTier targetTier;
        if (request.TierId.HasValue)
        {
            targetTier = tiers.FirstOrDefault(item => item.Id == request.TierId.Value)
                         ?? throw new BusinessException(ErrorCodes.NotFound, "会员等级不存在");
            targetTier.Name = normalizedName;
            targetTier.IconKey = normalizedIconKey;
            targetTier.ColorHex = normalizedColorHex;
            targetTier.UpgradeRuleType = normalizedRuleType;
            targetTier.UpgradeAmountThreshold = normalizedRuleType is "amount" or "both"
                ? decimal.Round(amountThreshold ?? 0, 2, MidpointRounding.AwayFromZero)
                : null;
            targetTier.UpgradeOrderCountThreshold = normalizedRuleType is "count" or "both"
                ? Math.Max(0, countThreshold ?? 0)
                : null;
            targetTier.DowngradeWindowDays = normalizedDowngradeWindowDays;
            targetTier.IsDefault = request.IsDefault;
            targetTier.SortOrder = normalizedSortOrder;
            targetTier.RequiredGrowth = ResolveRequiredGrowth(targetTier.UpgradeAmountThreshold, targetTier.UpgradeOrderCountThreshold);
            targetTier.BenefitsJson = MemberCenterSupport.SerializeBenefits(normalizedBenefits);
            await memberRepository.UpdateTierAsync(targetTier, cancellationToken);
        }
        else
        {
            targetTier = new MemberTier
            {
                Name = normalizedName,
                IconKey = normalizedIconKey,
                ColorHex = normalizedColorHex,
                UpgradeRuleType = normalizedRuleType,
                UpgradeAmountThreshold = normalizedRuleType is "amount" or "both"
                    ? decimal.Round(amountThreshold ?? 0, 2, MidpointRounding.AwayFromZero)
                    : null,
                UpgradeOrderCountThreshold = normalizedRuleType is "count" or "both"
                    ? Math.Max(0, countThreshold ?? 0)
                    : null,
                DowngradeWindowDays = normalizedDowngradeWindowDays,
                IsDefault = request.IsDefault,
                SortOrder = normalizedSortOrder,
                RequiredGrowth = ResolveRequiredGrowth(amountThreshold, countThreshold),
                BenefitsJson = MemberCenterSupport.SerializeBenefits(normalizedBenefits)
            };
            await memberRepository.AddTierAsync(targetTier, cancellationToken);
            tiers = tiers.Append(targetTier).ToList();
        }

        if (request.IsDefault)
        {
            foreach (var tier in tiers.Where(item => item.Id != targetTier.Id && item.IsDefault))
            {
                tier.IsDefault = false;
                await memberRepository.UpdateTierAsync(tier, cancellationToken);
            }
        }
        else
        {
            var hasDefault = tiers.Any(item => (item.Id == targetTier.Id ? targetTier.IsDefault : item.IsDefault));
            if (!hasDefault)
            {
                var fallbackTier = tiers
                    .Where(item => item.Id != targetTier.Id)
                    .OrderBy(item => item.SortOrder)
                    .ThenBy(item => item.Id)
                    .FirstOrDefault();
                if (fallbackTier is not null)
                {
                    fallbackTier.IsDefault = true;
                    await memberRepository.UpdateTierAsync(fallbackTier, cancellationToken);
                }
                else
                {
                    targetTier.IsDefault = true;
                    await memberRepository.UpdateTierAsync(targetTier, cancellationToken);
                }
            }
        }

        await memberRepository.SaveChangesAsync(cancellationToken);

        var profileCount = (await memberRepository.GetProfilesAsync(tenantId, cancellationToken)).Count(item => item.MemberTierId == targetTier.Id);
        var latestTiers = await memberRepository.GetTiersAsync(tenantId, cancellationToken);
        var canDelete = !targetTier.IsDefault && profileCount <= 0 && latestTiers.Count > 1;
        var latestTier = latestTiers.First(item => item.Id == targetTier.Id);
        return MemberCenterSupport.ToTierDetailDto(latestTier, canDelete);
    }

    private static string NormalizeColorHex(string? value)
    {
        var candidate = (value ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(candidate))
        {
            return "#999999";
        }

        if (!candidate.StartsWith('#'))
        {
            candidate = $"#{candidate}";
        }

        if (candidate.Length is not (4 or 7 or 9))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "等级颜色不合法");
        }

        return candidate.ToLowerInvariant();
    }

    private static int ResolveRequiredGrowth(decimal? amountThreshold, int? countThreshold)
    {
        var amountGrowth = amountThreshold.HasValue ? (int)Math.Clamp(decimal.Round(amountThreshold.Value, 0, MidpointRounding.AwayFromZero), 0m, int.MaxValue) : 0;
        var countGrowth = Math.Max(0, countThreshold ?? 0) * 100;
        return Math.Max(amountGrowth, countGrowth);
    }
}
