using System.Text.Json;

namespace Ehwrj.Core.Models;

public sealed record BattleMessage(string Text, bool? Enemy)
{
    public static IReadOnlyList<BattleMessage> FromJson(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return Array.Empty<BattleMessage>();
        }

        try
        {
            using var document = JsonDocument.Parse(json);
            var messages = new List<BattleMessage>();
            Collect(document.RootElement, messages);
            return messages
                .Where(static m => !string.IsNullOrWhiteSpace(m.Text))
                .DistinctBy(static m => $"{m.Enemy}:{m.Text}", StringComparer.Ordinal)
                .ToArray();
        }
        catch (JsonException)
        {
            return Array.Empty<BattleMessage>();
        }
    }

    private static void Collect(JsonElement element, List<BattleMessage> messages)
    {
        switch (element.ValueKind)
        {
            case JsonValueKind.Array:
                foreach (var item in element.EnumerateArray())
                {
                    Collect(item, messages);
                }
                break;
            case JsonValueKind.Object:
                if (TryCreate(element, out var message))
                {
                    messages.Add(message);
                }
                else
                {
                    foreach (var property in element.EnumerateObject())
                    {
                        if (property.Name is "items" or "events" or "damage" or "messages" or "gamechat" or "hudmsg")
                        {
                            Collect(property.Value, messages);
                        }
                    }
                }
                break;
            case JsonValueKind.String:
                messages.Add(new BattleMessage(Clean(element.GetString()), null));
                break;
        }
    }

    private static bool TryCreate(JsonElement element, out BattleMessage message)
    {
        var text = ReadString(element, "text", "msg", "message", "event", "title");
        if (string.IsNullOrWhiteSpace(text))
        {
            message = new BattleMessage(string.Empty, null);
            return false;
        }

        var enemy = ReadBool(element, "enemy", "is_enemy", "hostile");
        message = new BattleMessage(Clean(text), enemy);
        return true;
    }

    private static string? ReadString(JsonElement element, params string[] names)
    {
        foreach (var name in names)
        {
            if (!element.TryGetProperty(name, out var value))
            {
                continue;
            }

            return value.ValueKind == JsonValueKind.String ? value.GetString() : value.ToString();
        }

        return null;
    }

    private static bool? ReadBool(JsonElement element, params string[] names)
    {
        foreach (var name in names)
        {
            if (!element.TryGetProperty(name, out var value))
            {
                continue;
            }

            if (value.ValueKind is JsonValueKind.True or JsonValueKind.False)
            {
                return value.GetBoolean();
            }
        }

        return null;
    }

    private static string Clean(string? text)
    {
        if (string.IsNullOrWhiteSpace(text))
        {
            return string.Empty;
        }

        var result = text;
        while (true)
        {
            var start = result.IndexOf('<', StringComparison.Ordinal);
            var end = result.IndexOf('>', StringComparison.Ordinal);
            if (start < 0 || end <= start)
            {
                break;
            }

            result = result.Remove(start, end - start + 1);
        }

        return result.Trim();
    }
}

