using System.Globalization;
using System.Text.Json;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.Dto;
using TakeoutSaaS.Domain.Coupons.Entities;
using TakeoutSaaS.Domain.Coupons.Enums;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Application.App.Coupons.NewCustomer;

/// <summary>
/// 新客有礼映射与规则校验。
/// </summary>
internal static class NewCustomerMapping
{
    private static readonly HashSet<string> AllowedShareChannels =
    [
        "wechat_friend",
        "moments",
        "sms"
    ];

    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        PropertyNameCaseInsensitive = true
    };

    public static NewCustomerGiftType ParseGiftType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "coupon" => NewCustomerGiftType.Coupon,
            "direct" => NewCustomerGiftType.Direct,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "giftType 参数不合法")
        };
    }

    public static string ToGiftTypeText(NewCustomerGiftType value)
    {
        return value switch
        {
            NewCustomerGiftType.Coupon => "coupon",
            NewCustomerGiftType.Direct => "direct",
            _ => "coupon"
        };
    }

    public static NewCustomerCouponScene ParseCouponScene(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "welcome" => NewCustomerCouponScene.Welcome,
            "inviter" => NewCustomerCouponScene.InviterReward,
            "invitee" => NewCustomerCouponScene.InviteeReward,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "scene 参数不合法")
        };
    }

    public static string ToCouponSceneText(NewCustomerCouponScene value)
    {
        return value switch
        {
            NewCustomerCouponScene.Welcome => "welcome",
            NewCustomerCouponScene.InviterReward => "inviter",
            NewCustomerCouponScene.InviteeReward => "invitee",
            _ => "welcome"
        };
    }

    public static NewCustomerCouponType ParseCouponType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "amount_off" => NewCustomerCouponType.AmountOff,
            "discount" => NewCustomerCouponType.Discount,
            "free_shipping" => NewCustomerCouponType.FreeShipping,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "couponType 参数不合法")
        };
    }

    public static string ToCouponTypeText(NewCustomerCouponType value)
    {
        return value switch
        {
            NewCustomerCouponType.AmountOff => "amount_off",
            NewCustomerCouponType.Discount => "discount",
            NewCustomerCouponType.FreeShipping => "free_shipping",
            _ => "amount_off"
        };
    }

    public static NewCustomerInviteOrderStatus ParseInviteOrderStatus(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "pending_order" => NewCustomerInviteOrderStatus.PendingOrder,
            "ordered" => NewCustomerInviteOrderStatus.Ordered,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "orderStatus 参数不合法")
        };
    }

    public static string ToInviteOrderStatusText(NewCustomerInviteOrderStatus value)
    {
        return value switch
        {
            NewCustomerInviteOrderStatus.PendingOrder => "pending_order",
            NewCustomerInviteOrderStatus.Ordered => "ordered",
            _ => "pending_order"
        };
    }

    public static NewCustomerInviteRewardStatus ParseInviteRewardStatus(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "pending" => NewCustomerInviteRewardStatus.Pending,
            "issued" => NewCustomerInviteRewardStatus.Issued,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "rewardStatus 参数不合法")
        };
    }

    public static string ToInviteRewardStatusText(NewCustomerInviteRewardStatus value)
    {
        return value switch
        {
            NewCustomerInviteRewardStatus.Pending => "pending",
            NewCustomerInviteRewardStatus.Issued => "issued",
            _ => "pending"
        };
    }

    public static DateTime NormalizeUtc(DateTime value)
    {
        return value.Kind switch
        {
            DateTimeKind.Utc => value,
            DateTimeKind.Local => value.ToUniversalTime(),
            _ => DateTime.SpecifyKind(value, DateTimeKind.Utc)
        };
    }

    public static DateTime StartOfMonthUtc(DateTime nowUtc)
    {
        var utc = NormalizeUtc(nowUtc);
        return new DateTime(utc.Year, utc.Month, 1, 0, 0, 0, DateTimeKind.Utc);
    }

    public static decimal ToRatePercent(int numerator, int denominator)
    {
        if (denominator <= 0 || numerator <= 0)
        {
            return 0m;
        }

        return decimal.Round(numerator * 100m / denominator, 1, MidpointRounding.AwayFromZero);
    }

    public static decimal ToGrowthRatePercent(int currentValue, int previousValue)
    {
        if (previousValue <= 0)
        {
            return currentValue > 0 ? 100m : 0m;
        }

        return decimal.Round(
            (currentValue - previousValue) * 100m / previousValue,
            1,
            MidpointRounding.AwayFromZero);
    }

    public static string SerializeShareChannels(IReadOnlyCollection<string> channels)
    {
        return JsonSerializer.Serialize(channels, JsonOptions);
    }

    public static IReadOnlyList<string> DeserializeShareChannels(string? payload)
    {
        if (string.IsNullOrWhiteSpace(payload))
        {
            return [];
        }

        var values = JsonSerializer.Deserialize<List<string>>(payload, JsonOptions) ?? [];
        return NormalizeShareChannels(values);
    }

    public static IReadOnlyList<string> NormalizeShareChannels(IEnumerable<string>? values)
    {
        var normalized = (values ?? [])
            .Select(item => (item ?? string.Empty).Trim().ToLowerInvariant())
            .Where(item => !string.IsNullOrWhiteSpace(item))
            .Distinct()
            .ToList();

        if (normalized.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "shareChannels 不能为空");
        }

        if (normalized.Any(item => !AllowedShareChannels.Contains(item)))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "shareChannels 存在非法值");
        }

        return normalized;
    }

    public static IReadOnlyList<NewCustomerCouponRule> NormalizeCouponRulesForSave(
        long storeId,
        NewCustomerCouponScene scene,
        IReadOnlyCollection<NewCustomerSaveCouponRuleInputDto>? values)
    {
        var rules = (values ?? [])
            .Select((item, index) => NormalizeCouponRuleForSave(storeId, scene, item, index + 1))
            .ToList();

        return rules;
    }

    public static NewCustomerCouponRuleDto ToCouponRuleDto(NewCustomerCouponRule source)
    {
        return new NewCustomerCouponRuleDto
        {
            Id = source.Id,
            Scene = ToCouponSceneText(source.Scene),
            CouponType = ToCouponTypeText(source.CouponType),
            Value = source.Value,
            MinimumSpend = source.MinimumSpend,
            ValidDays = source.ValidDays,
            SortOrder = source.SortOrder
        };
    }

    public static NewCustomerInviteRecordDto ToInviteRecordDto(NewCustomerInviteRecord source)
    {
        return new NewCustomerInviteRecordDto
        {
            Id = source.Id,
            InviterName = source.InviterName,
            InviteeName = source.InviteeName,
            InviteTime = source.InviteTime,
            OrderStatus = ToInviteOrderStatusText(source.OrderStatus),
            RewardStatus = ToInviteRewardStatusText(source.RewardStatus),
            RewardIssuedAt = source.RewardIssuedAt,
            SourceChannel = source.SourceChannel
        };
    }

    public static NewCustomerGrowthRecordDto ToGrowthRecordDto(NewCustomerGrowthRecord source)
    {
        return new NewCustomerGrowthRecordDto
        {
            Id = source.Id,
            CustomerKey = source.CustomerKey,
            CustomerName = source.CustomerName,
            RegisteredAt = source.RegisteredAt,
            GiftClaimedAt = source.GiftClaimedAt,
            FirstOrderAt = source.FirstOrderAt,
            SourceChannel = source.SourceChannel
        };
    }

    public static string NormalizeDisplayName(string? value, string fieldName)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 不能为空");
        }

        if (normalized.Length > 64)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 长度不能超过 64");
        }

        return normalized;
    }

    public static string NormalizeCustomerKey(string? value)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "customerKey 不能为空");
        }

        if (normalized.Length > 64)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "customerKey 长度不能超过 64");
        }

        return normalized;
    }

    public static string? NormalizeOptionalText(string? value, string fieldName, int maxLength)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (normalized.Length == 0)
        {
            return null;
        }

        if (normalized.Length > maxLength)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 长度不能超过 {maxLength}");
        }

        return normalized;
    }

    private static NewCustomerCouponRule NormalizeCouponRuleForSave(
        long storeId,
        NewCustomerCouponScene scene,
        NewCustomerSaveCouponRuleInputDto value,
        int sortOrder)
    {
        ArgumentNullException.ThrowIfNull(value);

        var couponType = ParseCouponType(value.CouponType);
        var minimumSpend = NormalizeNonNegativeMoney(value.MinimumSpend, "minimumSpend");
        decimal? normalizedValue = couponType switch
        {
            NewCustomerCouponType.AmountOff => NormalizePositiveMoney(value.Value, "value"),
            NewCustomerCouponType.Discount => NormalizeDiscount(value.Value),
            NewCustomerCouponType.FreeShipping => null,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "couponType 参数不合法")
        };

        if (couponType == NewCustomerCouponType.AmountOff &&
            minimumSpend.HasValue &&
            normalizedValue.HasValue &&
            normalizedValue.Value >= minimumSpend.Value &&
            minimumSpend.Value > 0m)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "满减券 value 必须小于 minimumSpend");
        }

        if (value.ValidDays is < 1 or > 365)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "validDays 必须在 1-365 之间");
        }

        return new NewCustomerCouponRule
        {
            StoreId = storeId,
            Scene = scene,
            CouponType = couponType,
            Value = normalizedValue,
            MinimumSpend = minimumSpend,
            ValidDays = value.ValidDays,
            SortOrder = sortOrder
        };
    }

    private static decimal? NormalizeNonNegativeMoney(decimal? value, string fieldName)
    {
        if (!value.HasValue)
        {
            return null;
        }

        if (value.Value < 0m)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 不能小于 0");
        }

        return decimal.Round(value.Value, 2, MidpointRounding.AwayFromZero);
    }

    private static decimal NormalizePositiveMoney(decimal? value, string fieldName)
    {
        if (!value.HasValue || value.Value <= 0m)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 必须大于 0");
        }

        return decimal.Round(value.Value, 2, MidpointRounding.AwayFromZero);
    }

    private static decimal NormalizeDiscount(decimal? value)
    {
        if (!value.HasValue || value.Value <= 0m || value.Value >= 10m)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "折扣券 value 必须在 0-10 之间");
        }

        return decimal.Round(value.Value, 2, MidpointRounding.AwayFromZero);
    }
}
