using System.Text.Json;
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;

/// <summary>
/// 优惠券模板映射辅助。
/// </summary>
internal static class CouponTemplateMapping
{
    private const string StatusDisabled = "disabled";
    private const string StatusEnded = "ended";
    private const string StatusOngoing = "ongoing";
    private const string StatusUpcoming = "upcoming";

    private static readonly HashSet<string> AllowedChannels = ["delivery", "pickup", "dine_in"];

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

    public static bool TryParseCouponType(string? value, out CouponType couponType)
    {
        switch ((value ?? string.Empty).Trim().ToLowerInvariant())
        {
            case "amount_off":
                couponType = CouponType.AmountOff;
                return true;
            case "discount":
                couponType = CouponType.Percentage;
                return true;
            case "free_delivery":
                couponType = CouponType.DeliveryFee;
                return true;
            default:
                couponType = CouponType.AmountOff;
                return false;
        }
    }

    public static string ToCouponTypeText(CouponType value)
    {
        return value switch
        {
            CouponType.AmountOff => "amount_off",
            CouponType.Percentage => "discount",
            CouponType.DeliveryFee => "free_delivery",
            _ => throw new BusinessException(ErrorCodes.BadRequest, "存在不支持的券类型数据")
        };
    }

    public static bool TryParseEditorStatus(string? value, out CouponTemplateStatus status)
    {
        switch ((value ?? string.Empty).Trim().ToLowerInvariant())
        {
            case "enabled":
                status = CouponTemplateStatus.Active;
                return true;
            case "disabled":
                status = CouponTemplateStatus.Archived;
                return true;
            default:
                status = CouponTemplateStatus.Archived;
                return false;
        }
    }

    public static string ToEditorStatus(CouponTemplateStatus value)
    {
        return value == CouponTemplateStatus.Active ? "enabled" : "disabled";
    }

    public static bool TryNormalizeDisplayStatusFilter(string? value, out string? normalized)
    {
        var candidate = (value ?? string.Empty).Trim().ToLowerInvariant();
        if (string.IsNullOrWhiteSpace(candidate))
        {
            normalized = null;
            return true;
        }

        if (candidate is StatusOngoing or StatusUpcoming or StatusEnded or StatusDisabled)
        {
            normalized = candidate;
            return true;
        }

        normalized = null;
        return false;
    }

    public static string ResolveDisplayStatus(CouponTemplate template, DateTime nowUtc)
    {
        if (template.Status != CouponTemplateStatus.Active)
        {
            return StatusDisabled;
        }

        if (template.ValidFrom.HasValue && nowUtc < template.ValidFrom.Value)
        {
            return StatusUpcoming;
        }

        if (template.ValidTo.HasValue && nowUtc > template.ValidTo.Value)
        {
            return StatusEnded;
        }

        return StatusOngoing;
    }

    public static bool IsDimmedDisplayStatus(string displayStatus)
    {
        return string.Equals(displayStatus, StatusDisabled, StringComparison.OrdinalIgnoreCase)
            || string.Equals(displayStatus, StatusEnded, StringComparison.OrdinalIgnoreCase);
    }

    public static string ResolveValidityType(CouponTemplate template)
    {
        return template.RelativeValidDays.HasValue && template.RelativeValidDays.Value > 0
            ? "days"
            : "fixed";
    }

    public static IReadOnlyList<string> DeserializeChannels(string? channelsJson, long templateId)
    {
        if (string.IsNullOrWhiteSpace(channelsJson))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]渠道配置缺失");
        }

        var channels = JsonSerializer.Deserialize<List<string>>(channelsJson, JsonOptions)
            ?? throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]渠道配置异常");

        var normalized = channels
            .Select(item => (item ?? string.Empty).Trim().ToLowerInvariant())
            .Where(item => !string.IsNullOrWhiteSpace(item))
            .Distinct()
            .ToList();

        if (normalized.Count == 0 || normalized.Any(item => !AllowedChannels.Contains(item)))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]渠道配置异常");
        }

        return normalized;
    }

    public static IReadOnlyList<string> NormalizeChannelsForSave(IReadOnlyCollection<string>? channels)
    {
        var normalized = (channels ?? Array.Empty<string>())
            .Select(item => (item ?? string.Empty).Trim().ToLowerInvariant())
            .Where(item => !string.IsNullOrWhiteSpace(item))
            .Distinct()
            .ToList();

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

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

        return normalized;
    }

    public static CouponStoreScopeModel DeserializeStoreScope(string? storeScopeJson, long templateId)
    {
        if (string.IsNullOrWhiteSpace(storeScopeJson))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]门店范围配置缺失");
        }

        var payload = JsonSerializer.Deserialize<CouponStoreScopePayload>(storeScopeJson, JsonOptions)
            ?? throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]门店范围配置异常");

        var mode = (payload.Mode ?? string.Empty).Trim().ToLowerInvariant();
        var storeIds = (payload.StoreIds ?? [])
            .Where(id => id > 0)
            .Distinct()
            .ToList();

        if (mode is not ("all" or "stores"))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]门店范围配置异常");
        }

        if (storeIds.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"优惠券[{templateId}]门店范围配置异常");
        }

        return new CouponStoreScopeModel(mode, storeIds);
    }

    public static string SerializeStoreScope(string mode, IReadOnlyCollection<long> storeIds)
    {
        var normalizedMode = (mode ?? string.Empty).Trim().ToLowerInvariant();
        if (normalizedMode is not ("all" or "stores"))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeScopeMode 非法");
        }

        var normalizedStoreIds = storeIds
            .Where(id => id > 0)
            .Distinct()
            .ToList();
        if (normalizedStoreIds.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeIds 不能为空");
        }

        return JsonSerializer.Serialize(new CouponStoreScopePayload
        {
            Mode = normalizedMode,
            StoreIds = normalizedStoreIds
        }, JsonOptions);
    }

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

    private sealed class CouponStoreScopePayload
    {
        public string Mode { get; set; } = "stores";

        public List<long> StoreIds { get; set; } = [];
    }
}

/// <summary>
/// 优惠券门店范围快照。
/// </summary>
internal sealed record CouponStoreScopeModel(string Mode, IReadOnlyList<long> StoreIds);
