using System.Text.Json;
using TakeoutSaaS.Application.Dictionary.Abstractions;
using TakeoutSaaS.Application.Dictionary.Models;

namespace TakeoutSaaS.Infrastructure.Dictionary.ImportExport;

/// <summary>
/// JSON 字典导入解析器。
/// </summary>
public sealed class JsonDictionaryParser : IJsonDictionaryParser
{
    private static readonly JsonDocumentOptions DocumentOptions = new()
    {
        AllowTrailingCommas = true
    };

    /// <inheritdoc />
    public async Task<IReadOnlyList<DictionaryImportRow>> ParseAsync(Stream stream, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(stream);

        if (stream.CanSeek)
        {
            stream.Position = 0;
        }

        using var document = await JsonDocument.ParseAsync(stream, DocumentOptions, cancellationToken);
        if (document.RootElement.ValueKind != JsonValueKind.Array)
        {
            return Array.Empty<DictionaryImportRow>();
        }

        var rows = new List<DictionaryImportRow>();
        var index = 0;

        foreach (var element in document.RootElement.EnumerateArray())
        {
            cancellationToken.ThrowIfCancellationRequested();
            index++;

            rows.Add(new DictionaryImportRow
            {
                RowNumber = index,
                Code = ReadString(element, "code"),
                Key = ReadString(element, "key"),
                Value = ReadValue(element, "value"),
                SortOrder = ReadInt(element, "sortOrder"),
                IsEnabled = ReadBool(element, "isEnabled"),
                Description = ReadString(element, "description"),
                Source = ReadString(element, "source")
            });
        }

        return rows;
    }

    private static string? ReadString(JsonElement element, string propertyName)
    {
        if (!TryGetProperty(element, propertyName, out var value) || value.ValueKind == JsonValueKind.Null)
        {
            return null;
        }

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

    private static string? ReadValue(JsonElement element, string propertyName)
    {
        if (!TryGetProperty(element, propertyName, out var value) || value.ValueKind == JsonValueKind.Null)
        {
            return null;
        }

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

    private static int? ReadInt(JsonElement element, string propertyName)
    {
        if (!TryGetProperty(element, propertyName, out var value))
        {
            return null;
        }

        if (value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out var number))
        {
            return number;
        }

        if (value.ValueKind == JsonValueKind.String && int.TryParse(value.GetString(), out var parsed))
        {
            return parsed;
        }

        return null;
    }

    private static bool? ReadBool(JsonElement element, string propertyName)
    {
        if (!TryGetProperty(element, propertyName, out var value))
        {
            return null;
        }

        if (value.ValueKind == JsonValueKind.True || value.ValueKind == JsonValueKind.False)
        {
            return value.GetBoolean();
        }

        if (value.ValueKind == JsonValueKind.String && bool.TryParse(value.GetString(), out var parsed))
        {
            return parsed;
        }

        return null;
    }

    private static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement value)
    {
        foreach (var property in element.EnumerateObject())
        {
            if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
            {
                value = property.Value;
                return true;
            }
        }

        value = default;
        return false;
    }
}
