using System.Text.Json;
using TakeoutSaaS.Application.App.Coupons.PunchCard.Dto;
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;

namespace TakeoutSaaS.Application.App.Coupons.PunchCard;

/// <summary>
/// 次卡模块映射与标准化。
/// </summary>
internal static class PunchCardMapping
{
    private static readonly HashSet<string> AllowedNotifyChannels =
    [
        "in_app",
        "sms"
    ];

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

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

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

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

    public static PunchCardValidityType ParseValidityType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "days" => PunchCardValidityType.Days,
            "range" => PunchCardValidityType.DateRange,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "validityType 参数不合法")
        };
    }

    public static string ToValidityTypeText(PunchCardValidityType value)
    {
        return value switch
        {
            PunchCardValidityType.Days => "days",
            PunchCardValidityType.DateRange => "range",
            _ => "days"
        };
    }

    public static PunchCardScopeType ParseScopeType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "all" => PunchCardScopeType.All,
            "category" => PunchCardScopeType.Category,
            "tag" => PunchCardScopeType.Tag,
            "product" => PunchCardScopeType.Product,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "scopeType 参数不合法")
        };
    }

    public static string ToScopeTypeText(PunchCardScopeType value)
    {
        return value switch
        {
            PunchCardScopeType.All => "all",
            PunchCardScopeType.Category => "category",
            PunchCardScopeType.Tag => "tag",
            PunchCardScopeType.Product => "product",
            _ => "all"
        };
    }

    public static PunchCardUsageMode ParseUsageMode(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "free" => PunchCardUsageMode.Free,
            "cap" => PunchCardUsageMode.Cap,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "usageMode 参数不合法")
        };
    }

    public static string ToUsageModeText(PunchCardUsageMode value)
    {
        return value switch
        {
            PunchCardUsageMode.Free => "free",
            PunchCardUsageMode.Cap => "cap",
            _ => "free"
        };
    }

    public static PunchCardExpireStrategy ParseExpireStrategy(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "invalidate" => PunchCardExpireStrategy.Invalidate,
            "refund" => PunchCardExpireStrategy.Refund,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "expireStrategy 参数不合法")
        };
    }

    public static string ToExpireStrategyText(PunchCardExpireStrategy value)
    {
        return value switch
        {
            PunchCardExpireStrategy.Invalidate => "invalidate",
            PunchCardExpireStrategy.Refund => "refund",
            _ => "invalidate"
        };
    }

    public static PunchCardUsageRecordFilterStatus? ParseUsageStatusFilter(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "" => null,
            "normal" => PunchCardUsageRecordFilterStatus.Normal,
            "used_up" => PunchCardUsageRecordFilterStatus.UsedUp,
            "expired" => PunchCardUsageRecordFilterStatus.Expired,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "status 参数不合法")
        };
    }

    public static string ToUsageDisplayStatusText(PunchCardUsageRecordStatus value)
    {
        return value switch
        {
            PunchCardUsageRecordStatus.Normal => "normal",
            PunchCardUsageRecordStatus.AlmostUsedUp => "almost_used_up",
            PunchCardUsageRecordStatus.UsedUp => "used_up",
            PunchCardUsageRecordStatus.Expired => "expired",
            _ => "normal"
        };
    }

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

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

        return normalized;
    }

    public static string NormalizeOptionalCoverUrl(string? value)
    {
        var normalized = (value ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            return string.Empty;
        }

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

        return normalized;
    }

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

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

        return normalized;
    }

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

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

        return normalized;
    }

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

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

        return normalized;
    }

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

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

        return normalized;
    }

    public static decimal NormalizeAmount(decimal value, string fieldName, bool allowZero = false)
    {
        if (value < 0 || (!allowZero && value <= 0))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 参数不合法");
        }

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

    public static decimal? NormalizeOptionalAmount(decimal? value, string fieldName, bool allowZero = true)
    {
        if (!value.HasValue)
        {
            return null;
        }

        if (value.Value < 0 || (!allowZero && value.Value <= 0))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 参数不合法");
        }

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

    public static int NormalizeRequiredPositiveInt(int value, string fieldName, int max = 100_000)
    {
        if (value <= 0 || value > max)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 参数不合法");
        }

        return value;
    }

    public static int? NormalizeOptionalLimit(int? value, string fieldName, int max = 100_000)
    {
        if (!value.HasValue || value.Value <= 0)
        {
            return null;
        }

        if (value.Value > max)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 参数不合法");
        }

        return value;
    }

    public static (int? ValidityDays, DateTime? ValidFrom, DateTime? ValidTo) NormalizeValidity(
        PunchCardValidityType validityType,
        int? validityDays,
        DateTime? validFrom,
        DateTime? validTo)
    {
        return validityType switch
        {
            PunchCardValidityType.Days =>
                (
                    NormalizeRequiredPositiveInt(validityDays ?? 0, "validityDays", 3650),
                    null,
                    null
                ),
            PunchCardValidityType.DateRange => NormalizeRange(validFrom, validTo),
            _ => throw new BusinessException(ErrorCodes.BadRequest, "validityType 参数不合法")
        };
    }

    public static (IReadOnlyList<long> CategoryIds, IReadOnlyList<long> TagIds, IReadOnlyList<long> ProductIds) NormalizeScopeIds(
        PunchCardScopeType scopeType,
        IReadOnlyCollection<long>? categoryIds,
        IReadOnlyCollection<long>? tagIds,
        IReadOnlyCollection<long>? productIds)
    {
        var normalizedCategoryIds = NormalizeSnowflakeIds(categoryIds, "scopeCategoryIds", false);
        var normalizedTagIds = NormalizeSnowflakeIds(tagIds, "scopeTagIds", false);
        var normalizedProductIds = NormalizeSnowflakeIds(productIds, "scopeProductIds", false);

        return scopeType switch
        {
            PunchCardScopeType.All => ([], [], []),
            PunchCardScopeType.Category =>
                normalizedCategoryIds.Count == 0
                    ? throw new BusinessException(ErrorCodes.BadRequest, "scopeCategoryIds 不能为空")
                    : (normalizedCategoryIds, [], []),
            PunchCardScopeType.Tag =>
                normalizedTagIds.Count == 0
                    ? throw new BusinessException(ErrorCodes.BadRequest, "scopeTagIds 不能为空")
                    : ([], normalizedTagIds, []),
            PunchCardScopeType.Product =>
                normalizedProductIds.Count == 0
                    ? throw new BusinessException(ErrorCodes.BadRequest, "scopeProductIds 不能为空")
                    : ([], [], normalizedProductIds),
            _ => throw new BusinessException(ErrorCodes.BadRequest, "scopeType 参数不合法")
        };
    }

    public static IReadOnlyList<string> NormalizeNotifyChannels(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, "notifyChannels 不能为空");
        }

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

        return normalized;
    }

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

        var values = JsonSerializer.Deserialize<List<string>>(payload, JsonOptions) ?? [];
        return values
            .Select(item => (item ?? string.Empty).Trim().ToLowerInvariant())
            .Where(item => AllowedNotifyChannels.Contains(item))
            .Distinct()
            .ToList();
    }

    public static string SerializeNotifyChannels(IEnumerable<string>? values)
    {
        return JsonSerializer.Serialize(NormalizeNotifyChannels(values), JsonOptions);
    }

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

        var values = JsonSerializer.Deserialize<List<long>>(payload, JsonOptions) ?? [];
        return values
            .Where(id => id > 0)
            .Distinct()
            .OrderBy(id => id)
            .ToList();
    }

    public static string SerializeSnowflakeIds(IEnumerable<long>? values)
    {
        return JsonSerializer.Serialize(NormalizeSnowflakeIds(values, "ids", false), JsonOptions);
    }

    public static string BuildValiditySummary(PunchCardTemplate template)
    {
        return template.ValidityType switch
        {
            PunchCardValidityType.Days => $"{template.ValidityDays ?? 0}天有效",
            PunchCardValidityType.DateRange when template.ValidFrom.HasValue && template.ValidTo.HasValue =>
                $"{template.ValidFrom.Value:yyyy-MM-dd} 至 {template.ValidTo.Value:yyyy-MM-dd}",
            _ => "-"
        };
    }

    public static DateTime ResolveInstanceExpireAt(PunchCardTemplate template, DateTime purchasedAtUtc)
    {
        var purchasedAt = NormalizeUtc(purchasedAtUtc);

        return template.ValidityType switch
        {
            PunchCardValidityType.Days => purchasedAt.Date.AddDays(template.ValidityDays ?? 0).AddTicks(-1),
            PunchCardValidityType.DateRange => template.ValidTo ?? purchasedAt.Date.AddTicks(-1),
            _ => purchasedAt.Date.AddTicks(-1)
        };
    }

    public static bool IsInstanceExpired(PunchCardInstance instance, DateTime nowUtc)
    {
        var utcNow = NormalizeUtc(nowUtc);
        if (instance.Status == PunchCardInstanceStatus.Expired)
        {
            return true;
        }

        return instance.ExpiresAt.HasValue && instance.ExpiresAt.Value < utcNow;
    }

    public static PunchCardUsageRecordStatus ResolveUsageRecordStatus(
        PunchCardInstance instance,
        int remainingTimes,
        DateTime usedAtUtc)
    {
        if (IsInstanceExpired(instance, usedAtUtc))
        {
            return PunchCardUsageRecordStatus.Expired;
        }

        if (remainingTimes <= 0)
        {
            return PunchCardUsageRecordStatus.UsedUp;
        }

        return remainingTimes <= 2
            ? PunchCardUsageRecordStatus.AlmostUsedUp
            : PunchCardUsageRecordStatus.Normal;
    }

    private static IReadOnlyList<long> NormalizeSnowflakeIds(
        IEnumerable<long>? values,
        string fieldName,
        bool required)
    {
        var normalized = (values ?? [])
            .Where(id => id > 0)
            .Distinct()
            .OrderBy(id => id)
            .ToList();

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

        return normalized;
    }

    private static (int? ValidityDays, DateTime? ValidFrom, DateTime? ValidTo) NormalizeRange(
        DateTime? validFrom,
        DateTime? validTo)
    {
        if (!validFrom.HasValue || !validTo.HasValue)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "validFrom / validTo 不能为空");
        }

        var normalizedFrom = NormalizeUtc(validFrom.Value).Date;
        var normalizedTo = NormalizeUtc(validTo.Value).Date.AddDays(1).AddTicks(-1);

        if (normalizedFrom > normalizedTo)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "validFrom 不能晚于 validTo");
        }

        return (null, normalizedFrom, normalizedTo);
    }
}
