using System.Globalization;
using System.Text.Json;

namespace Ehwrj.Core.Models;

public sealed class FlightState
{
    public static FlightState Empty { get; } = new(new Dictionary<string, string>(StringComparer.Ordinal), "{}");

    public FlightState(IReadOnlyDictionary<string, string> values, string rawJson)
    {
        Values = values;
        RawJson = rawJson;
    }

    public IReadOnlyDictionary<string, string> Values { get; }
    public string RawJson { get; }
    public double? TrueAirspeedKmh => GetNumber("TAS, km/h", "TAS", "true_air_speed");
    public double? IndicatedAirspeedKmh => GetNumber("IAS, km/h", "IAS", "indicated_air_speed");
    public double? VerticalSpeedMetersPerSecond => GetNumber("Vy, m/s", "Vy", "vertical_speed");
    public double? AltitudeMeters => GetNumber("H, m", "H", "altitude");
    public double? Mach => CalculateMach();
    public double? ClimbAngleDegrees => CalculateClimbAngle();

    public static FlightState FromJson(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return Empty;
        }

        try
        {
            using var document = JsonDocument.Parse(json);
            var values = new Dictionary<string, string>(StringComparer.Ordinal);
            Flatten(document.RootElement, values, prefix: null);
            return new FlightState(values, json);
        }
        catch (JsonException)
        {
            return Empty;
        }
    }

    public double? GetNumber(params string[] names)
    {
        foreach (var name in names)
        {
            if (!Values.TryGetValue(name, out var text))
            {
                continue;
            }

            if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
            {
                return value;
            }

            var normalized = text.Replace(',', '.');
            if (double.TryParse(normalized, NumberStyles.Float, CultureInfo.InvariantCulture, out value))
            {
                return value;
            }
        }

        return null;
    }

    private double? CalculateMach()
    {
        var kmh = TrueAirspeedKmh ?? IndicatedAirspeedKmh ?? GetNumber("M, km/h");
        if (!kmh.HasValue || kmh.Value <= 0)
        {
            return null;
        }

        return Math.Clamp(kmh.Value / 3.6 / 343.0, 0, 5);
    }

    private double? CalculateClimbAngle()
    {
        var kmh = TrueAirspeedKmh ?? IndicatedAirspeedKmh;
        var vertical = VerticalSpeedMetersPerSecond;
        if (!kmh.HasValue || !vertical.HasValue || kmh.Value <= 1)
        {
            return null;
        }

        var speedMs = kmh.Value / 3.6;
        var ratio = Math.Clamp(vertical.Value / speedMs, -1, 1);
        return Math.Asin(ratio) * 180.0 / Math.PI;
    }

    private static void Flatten(JsonElement element, Dictionary<string, string> values, string? prefix)
    {
        switch (element.ValueKind)
        {
            case JsonValueKind.Object:
                foreach (var property in element.EnumerateObject())
                {
                    var name = prefix is null ? property.Name : $"{prefix}.{property.Name}";
                    Flatten(property.Value, values, name);
                }
                break;
            case JsonValueKind.Array:
                var index = 0;
                foreach (var item in element.EnumerateArray())
                {
                    Flatten(item, values, prefix is null ? $"[{index}]" : $"{prefix}[{index}]");
                    index++;
                }
                break;
            case JsonValueKind.String:
                if (prefix is not null) values[prefix] = element.GetString() ?? string.Empty;
                break;
            case JsonValueKind.Number:
            case JsonValueKind.True:
            case JsonValueKind.False:
                if (prefix is not null) values[prefix] = element.ToString();
                break;
        }
    }
}

