using System.Globalization;
using System.Text.Json;
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Membership.Enums;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Application.App.Members.PointsMall;

/// <summary>
/// 积分商城模块映射与标准化工具。
/// </summary>
internal static class MemberPointMallMapping
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        PropertyNameCaseInsensitive = true
    };

    public static MemberPointMallExpiryMode ParseExpiryMode(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "permanent" => MemberPointMallExpiryMode.Permanent,
            "yearly_clear" => MemberPointMallExpiryMode.YearlyClear,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "expiryMode 参数不合法")
        };
    }

    public static string ToExpiryModeText(MemberPointMallExpiryMode value)
    {
        return value switch
        {
            MemberPointMallExpiryMode.Permanent => "permanent",
            MemberPointMallExpiryMode.YearlyClear => "yearly_clear",
            _ => "yearly_clear"
        };
    }

    public static MemberPointMallRedeemType ParseRedeemType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "product" => MemberPointMallRedeemType.Product,
            "coupon" => MemberPointMallRedeemType.Coupon,
            "physical" => MemberPointMallRedeemType.Physical,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "redeemType 参数不合法")
        };
    }

    public static MemberPointMallRedeemType? TryParseRedeemType(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return ParseRedeemType(value);
    }

    public static string ToRedeemTypeText(MemberPointMallRedeemType value)
    {
        return value switch
        {
            MemberPointMallRedeemType.Product => "product",
            MemberPointMallRedeemType.Coupon => "coupon",
            MemberPointMallRedeemType.Physical => "physical",
            _ => "product"
        };
    }

    public static string ToRedeemTypeDisplayText(MemberPointMallRedeemType value)
    {
        return value switch
        {
            MemberPointMallRedeemType.Product => "商品",
            MemberPointMallRedeemType.Coupon => "优惠券",
            MemberPointMallRedeemType.Physical => "实物",
            _ => "未知"
        };
    }

    public static MemberPointMallExchangeType ParseExchangeType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "points" => MemberPointMallExchangeType.PointsOnly,
            "mixed" => MemberPointMallExchangeType.PointsAndCash,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "exchangeType 参数不合法")
        };
    }

    public static string ToExchangeTypeText(MemberPointMallExchangeType value)
    {
        return value switch
        {
            MemberPointMallExchangeType.PointsOnly => "points",
            MemberPointMallExchangeType.PointsAndCash => "mixed",
            _ => "points"
        };
    }

    public static MemberPointMallProductStatus ParseProductStatus(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "enabled" => MemberPointMallProductStatus.Enabled,
            "disabled" => MemberPointMallProductStatus.Disabled,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "status 参数不合法")
        };
    }

    public static MemberPointMallProductStatus? TryParseProductStatus(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return ParseProductStatus(value);
    }

    public static string ToProductStatusText(MemberPointMallProductStatus value)
    {
        return value switch
        {
            MemberPointMallProductStatus.Enabled => "enabled",
            MemberPointMallProductStatus.Disabled => "disabled",
            _ => "disabled"
        };
    }

    public static MemberPointMallPickupMethod ParsePickupMethod(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "store_pickup" => MemberPointMallPickupMethod.StorePickup,
            "delivery" => MemberPointMallPickupMethod.Delivery,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "pickupMethod 参数不合法")
        };
    }

    public static string ToPickupMethodText(MemberPointMallPickupMethod value)
    {
        return value switch
        {
            MemberPointMallPickupMethod.StorePickup => "store_pickup",
            MemberPointMallPickupMethod.Delivery => "delivery",
            _ => "store_pickup"
        };
    }

    public static MemberPointMallVerifyMethod ParseVerifyMethod(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "scan" => MemberPointMallVerifyMethod.Scan,
            "manual" => MemberPointMallVerifyMethod.Manual,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "verifyMethod 参数不合法")
        };
    }

    public static string ToVerifyMethodText(MemberPointMallVerifyMethod value)
    {
        return value switch
        {
            MemberPointMallVerifyMethod.Scan => "scan",
            MemberPointMallVerifyMethod.Manual => "manual",
            _ => "manual"
        };
    }

    public static string ToVerifyMethodDisplayText(MemberPointMallVerifyMethod value)
    {
        return value switch
        {
            MemberPointMallVerifyMethod.Scan => "扫码核销",
            MemberPointMallVerifyMethod.Manual => "手动核销",
            _ => "未知"
        };
    }

    public static MemberPointMallRecordStatus ParseRecordStatus(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "pending_pickup" => MemberPointMallRecordStatus.PendingPickup,
            "issued" => MemberPointMallRecordStatus.Issued,
            "completed" => MemberPointMallRecordStatus.Completed,
            "canceled" => MemberPointMallRecordStatus.Canceled,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "status 参数不合法")
        };
    }

    public static MemberPointMallRecordStatus? TryParseRecordStatus(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return ParseRecordStatus(value);
    }

    public static string ToRecordStatusText(MemberPointMallRecordStatus value)
    {
        return value switch
        {
            MemberPointMallRecordStatus.PendingPickup => "pending_pickup",
            MemberPointMallRecordStatus.Issued => "issued",
            MemberPointMallRecordStatus.Completed => "completed",
            MemberPointMallRecordStatus.Canceled => "canceled",
            _ => "issued"
        };
    }

    public static string ToRecordStatusDisplayText(MemberPointMallRecordStatus value)
    {
        return value switch
        {
            MemberPointMallRecordStatus.PendingPickup => "待领取",
            MemberPointMallRecordStatus.Issued => "已发放",
            MemberPointMallRecordStatus.Completed => "已完成",
            MemberPointMallRecordStatus.Canceled => "已取消",
            _ => "未知"
        };
    }

    public static MemberPointMallNotifyChannel ParseNotifyChannel(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "in_app" => MemberPointMallNotifyChannel.InApp,
            "sms" => MemberPointMallNotifyChannel.Sms,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "notifyChannels 参数不合法")
        };
    }

    public static string ToNotifyChannelText(MemberPointMallNotifyChannel value)
    {
        return value switch
        {
            MemberPointMallNotifyChannel.InApp => "in_app",
            MemberPointMallNotifyChannel.Sms => "sms",
            _ => "in_app"
        };
    }

    public static IReadOnlyList<MemberPointMallNotifyChannel> ParseNotifyChannels(
        IReadOnlyCollection<string>? values)
    {
        var parsed = (values ?? Array.Empty<string>())
            .Select(ParseNotifyChannel)
            .Distinct()
            .ToList();

        if (parsed.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "notifyChannels 至少选择一项");
        }

        return parsed;
    }

    public static IReadOnlyList<MemberPointMallNotifyChannel> DeserializeNotifyChannels(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return [];
        }

        try
        {
            var source = JsonSerializer.Deserialize<List<string>>(value, JsonOptions) ?? [];
            var channels = source
                .Select(item =>
                {
                    try
                    {
                        return (MemberPointMallNotifyChannel?)ParseNotifyChannel(item);
                    }
                    catch
                    {
                        return null;
                    }
                })
                .Where(item => item.HasValue)
                .Select(item => item!.Value)
                .Distinct()
                .ToList();

            return channels;
        }
        catch
        {
            return [];
        }
    }

    public static string SerializeNotifyChannels(IReadOnlyCollection<MemberPointMallNotifyChannel> values)
    {
        var payload = (values ?? Array.Empty<MemberPointMallNotifyChannel>())
            .Distinct()
            .OrderBy(item => item)
            .Select(ToNotifyChannelText)
            .ToList();

        return JsonSerializer.Serialize(payload, JsonOptions);
    }

    public static string NormalizeName(string? value, string fieldName = "name")
    {
        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? NormalizePhysicalName(string? value)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "physicalName 不能为空");
        }

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

        return normalized;
    }

    public static string? NormalizeImageUrl(string? value)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (normalized.Length == 0)
        {
            return null;
        }

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

        return normalized;
    }

    public static string? NormalizeDescription(string? value)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (normalized.Length == 0)
        {
            return null;
        }

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

        return normalized;
    }

    public static string? NormalizeKeyword(string? value)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (normalized.Length == 0)
        {
            return null;
        }

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

        return normalized;
    }

    public static string? NormalizeRemark(string? value, string fieldName = "remark")
    {
        var normalized = (value ?? string.Empty).Trim();
        if (normalized.Length == 0)
        {
            return null;
        }

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

        return normalized;
    }

    public static int NormalizePositiveInt(int value, string fieldName)
    {
        if (value <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 必须大于 0");
        }

        return value;
    }

    public static int NormalizeRequiredPoints(int value)
    {
        if (value <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "requiredPoints 必须大于 0");
        }

        if (value > 1_000_000)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "requiredPoints 不能超过 1000000");
        }

        return value;
    }

    public static int NormalizeStockTotal(int value)
    {
        if (value < 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "stockTotal 不能小于 0");
        }

        if (value > 10_000_000)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "stockTotal 不能超过 10000000");
        }

        return value;
    }

    public static int? NormalizePerMemberLimit(int? value)
    {
        if (!value.HasValue || value.Value <= 0)
        {
            return null;
        }

        if (value.Value > 9999)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "perMemberLimit 不能超过 9999");
        }

        return value.Value;
    }

    public static decimal NormalizeCashAmount(decimal value, MemberPointMallExchangeType exchangeType)
    {
        var normalized = decimal.Round(value, 2, MidpointRounding.AwayFromZero);
        if (exchangeType == MemberPointMallExchangeType.PointsOnly)
        {
            return 0m;
        }

        if (normalized <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "cashAmount 必须大于 0");
        }

        return normalized;
    }

    public static (DateTime? StartUtc, DateTime? EndUtc) NormalizeDateRange(DateTime? startUtc, DateTime? endUtc)
    {
        DateTime? normalizedStart = null;
        DateTime? normalizedEnd = null;

        if (startUtc.HasValue)
        {
            var utc = NormalizeUtc(startUtc.Value);
            normalizedStart = new DateTime(utc.Year, utc.Month, utc.Day, 0, 0, 0, DateTimeKind.Utc);
        }

        if (endUtc.HasValue)
        {
            var utc = NormalizeUtc(endUtc.Value);
            normalizedEnd = new DateTime(utc.Year, utc.Month, utc.Day, 0, 0, 0, DateTimeKind.Utc)
                .AddDays(1)
                .AddTicks(-1);
        }

        if (normalizedStart.HasValue && normalizedEnd.HasValue && normalizedStart.Value > normalizedEnd.Value)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "开始日期不能晚于结束日期");
        }

        return (normalizedStart, normalizedEnd);
    }

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

    public static string ResolveMemberName(MemberProfile member)
    {
        var nickname = (member.Nickname ?? string.Empty).Trim();
        if (!string.IsNullOrWhiteSpace(nickname))
        {
            return nickname.Length <= 64 ? nickname : nickname[..64];
        }

        var mobile = NormalizePhone(member.Mobile);
        return mobile.Length >= 4 ? $"会员{mobile[^4..]}" : "会员";
    }

    public static string ResolveMemberMobileMasked(MemberProfile member)
    {
        return MaskPhone(NormalizePhone(member.Mobile));
    }

    public static string BuildRecordNo(DateTime nowUtc)
    {
        var utcNow = NormalizeUtc(nowUtc);
        return $"PT{utcNow:yyyyMMddHHmmssfff}{Random.Shared.Next(1000, 9999)}";
    }

    public static MemberPointMallRecordStatus ResolveRecordInitialStatus(MemberPointMallRedeemType redeemType)
    {
        return redeemType == MemberPointMallRedeemType.Physical
            ? MemberPointMallRecordStatus.PendingPickup
            : MemberPointMallRecordStatus.Issued;
    }

    public static DateTime? ResolveRecordInitialIssuedAt(MemberPointMallRedeemType redeemType, DateTime redeemedAt)
    {
        return redeemType == MemberPointMallRedeemType.Physical ? null : redeemedAt;
    }

    private static string NormalizePhone(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        var chars = value.Where(char.IsDigit).ToArray();
        return chars.Length == 0 ? string.Empty : new string(chars);
    }

    private static string MaskPhone(string normalizedPhone)
    {
        if (normalizedPhone.Length >= 11)
        {
            return $"{normalizedPhone[..3]}****{normalizedPhone[^4..]}";
        }

        if (normalizedPhone.Length >= 7)
        {
            return $"{normalizedPhone[..3]}***{normalizedPhone[^2..]}";
        }

        return normalizedPhone;
    }
}
