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

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

    internal static MemberMessageAudienceType ParseAudienceType(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "all" => MemberMessageAudienceType.All,
            "tag" => MemberMessageAudienceType.Tags,
            "tags" => MemberMessageAudienceType.Tags,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "audienceType 非法")
        };
    }

    internal static string ToAudienceTypeText(MemberMessageAudienceType value)
    {
        return value switch
        {
            MemberMessageAudienceType.All => "all",
            MemberMessageAudienceType.Tags => "tag",
            _ => "all"
        };
    }

    internal static MemberMessageScheduleType ParseScheduleType(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "immediate" => MemberMessageScheduleType.Immediate,
            "scheduled" => MemberMessageScheduleType.Scheduled,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "scheduleType 非法")
        };
    }

    internal static string ToScheduleTypeText(MemberMessageScheduleType value)
    {
        return value switch
        {
            MemberMessageScheduleType.Immediate => "immediate",
            MemberMessageScheduleType.Scheduled => "scheduled",
            _ => "immediate"
        };
    }

    internal static MemberMessageStatus ParseStatusOrNull(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "draft" => MemberMessageStatus.Draft,
            "pending" => MemberMessageStatus.Pending,
            "sending" => MemberMessageStatus.Sending,
            "sent" => MemberMessageStatus.Sent,
            "failed" => MemberMessageStatus.Failed,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "status 非法")
        };
    }

    internal static MemberMessageStatus? TryParseStatus(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return ParseStatusOrNull(value);
    }

    internal static string ToStatusText(MemberMessageStatus value)
    {
        return value switch
        {
            MemberMessageStatus.Draft => "draft",
            MemberMessageStatus.Pending => "pending",
            MemberMessageStatus.Sending => "sending",
            MemberMessageStatus.Sent => "sent",
            MemberMessageStatus.Failed => "failed",
            _ => "draft"
        };
    }

    internal static MemberMessageChannel ParseChannel(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "inapp" => MemberMessageChannel.InApp,
            "sms" => MemberMessageChannel.Sms,
            "wechat-mini" => MemberMessageChannel.WeChatMini,
            "wechat" => MemberMessageChannel.WeChatMini,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "channel 非法")
        };
    }

    internal static MemberMessageChannel? TryParseChannel(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return ParseChannel(value);
    }

    internal static string ToChannelText(MemberMessageChannel value)
    {
        return value switch
        {
            MemberMessageChannel.InApp => "inapp",
            MemberMessageChannel.Sms => "sms",
            MemberMessageChannel.WeChatMini => "wechat-mini",
            _ => "inapp"
        };
    }

    internal static string ToRecipientStatusText(MemberMessageRecipientStatus value)
    {
        return value switch
        {
            MemberMessageRecipientStatus.Pending => "pending",
            MemberMessageRecipientStatus.Sent => "sent",
            MemberMessageRecipientStatus.Failed => "failed",
            _ => "pending"
        };
    }

    internal static MemberMessageTemplateCategory ParseTemplateCategory(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "marketing" => MemberMessageTemplateCategory.Marketing,
            "notice" => MemberMessageTemplateCategory.Notice,
            "recall" => MemberMessageTemplateCategory.Recall,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "category 非法")
        };
    }

    internal static MemberMessageTemplateCategory? TryParseTemplateCategory(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return ParseTemplateCategory(value);
    }

    internal static string ToTemplateCategoryText(MemberMessageTemplateCategory value)
    {
        return value switch
        {
            MemberMessageTemplateCategory.Marketing => "marketing",
            MemberMessageTemplateCategory.Notice => "notice",
            MemberMessageTemplateCategory.Recall => "recall",
            _ => "notice"
        };
    }

    internal static IReadOnlyList<string> NormalizeTags(IReadOnlyList<string>? tags)
    {
        return (tags ?? [])
            .Select(item => (item ?? string.Empty).Trim())
            .Where(item => !string.IsNullOrWhiteSpace(item))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .OrderBy(item => item, StringComparer.OrdinalIgnoreCase)
            .ToList();
    }

    internal static IReadOnlyList<string> NormalizeChannels(IReadOnlyList<string>? channels)
    {
        var parsed = (channels ?? [])
            .Select(ParseChannel)
            .Distinct()
            .ToList();

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

        return parsed.Select(ToChannelText).ToList();
    }

    internal static string SerializeStringArray(IReadOnlyList<string> source)
    {
        return JsonSerializer.Serialize(source, JsonOptions);
    }

    internal static IReadOnlyList<string> DeserializeStringArray(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return [];
        }

        try
        {
            return JsonSerializer.Deserialize<List<string>>(value, JsonOptions)?
                .Select(item => (item ?? string.Empty).Trim())
                .Where(item => !string.IsNullOrWhiteSpace(item))
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToList() ?? [];
        }
        catch
        {
            return [];
        }
    }

    internal static decimal ResolveRatePercent(int numerator, int denominator)
    {
        if (denominator <= 0 || numerator <= 0)
        {
            return 0;
        }

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

    internal static MemberMessageTemplateDto ToTemplateDto(MemberMessageTemplate source)
    {
        return new MemberMessageTemplateDto
        {
            TemplateId = source.Id,
            Name = source.Name,
            Category = ToTemplateCategoryText(source.Category),
            Content = source.Content,
            UsageCount = source.UsageCount,
            LastUsedAt = source.LastUsedAt
        };
    }

    internal static MemberMessageReachRecipientDto ToRecipientDto(MemberReachRecipient source)
    {
        return new MemberMessageReachRecipientDto
        {
            MemberId = source.MemberId,
            Channel = ToChannelText(source.Channel),
            Status = ToRecipientStatusText(source.Status),
            Mobile = source.Mobile,
            OpenId = source.OpenId,
            SentAt = source.SentAt,
            ReadAt = source.ReadAt,
            ConvertedAt = source.ConvertedAt,
            ErrorMessage = source.ErrorMessage
        };
    }
}

