using System.Text.Json;
using Microsoft.Extensions.Options;
using MiGu.Server.Launcher;

namespace MiGu.Server.Ota;

public sealed class OtaVehicleSource
{
    private readonly IHttpClientFactory _httpFactory;
    private readonly SimpleLiteOptions _sl;
    private readonly InternalTokenStoreAccessor _token;
    private readonly ILogger<OtaVehicleSource> _log;

    public OtaVehicleSource(
        IHttpClientFactory httpFactory,
        IOptions<SimpleLiteOptions> sl,
        InternalTokenStoreAccessor token,
        ILogger<OtaVehicleSource> log)
    {
        _httpFactory = httpFactory;
        _sl = sl.Value;
        _token = token;
        _log = log;
    }

    public async Task<List<OtaVehicleRow>> ListCarsAsync(CancellationToken ct)
    {
        var port = _sl.ProjectionPort > 0 ? _sl.ProjectionPort : 8222;
        var cars = await TryProjectionAsync(port, ct);
        if (cars.Count == 0)
            cars = await TryAgvListAsync(port, ct);
        return cars;
    }

    private async Task<List<OtaVehicleRow>> TryProjectionAsync(int port, CancellationToken ct)
    {
        try
        {
            using var client = _httpFactory.CreateClient();
            client.Timeout = TimeSpan.FromSeconds(8);
            using var req = new HttpRequestMessage(HttpMethod.Get, $"http://127.0.0.1:{port}/projection/cars");
            var token = _token.Token;
            if (!string.IsNullOrEmpty(token))
                req.Headers.TryAddWithoutValidation("X-Platform-Internal-Token", token);
            using var resp = await client.SendAsync(req, ct);
            if (!resp.IsSuccessStatusCode) return new();
            var text = await resp.Content.ReadAsStringAsync(ct);
            return ParseCars(text);
        }
        catch (Exception ex)
        {
            _log.LogDebug(ex, "projection/cars failed");
            return new();
        }
    }

    private async Task<List<OtaVehicleRow>> TryAgvListAsync(int port, CancellationToken ct)
    {
        try
        {
            using var client = _httpFactory.CreateClient();
            client.Timeout = TimeSpan.FromSeconds(8);
            using var resp = await client.GetAsync($"http://127.0.0.1:{port}/api/agv/list", ct);
            if (!resp.IsSuccessStatusCode) return new();
            var text = await resp.Content.ReadAsStringAsync(ct);
            using var doc = JsonDocument.Parse(text);
            var root = doc.RootElement;
            var arr = root.ValueKind == JsonValueKind.Array ? root
                : root.TryGetProperty("data", out var d) ? d
                : root.TryGetProperty("items", out var i) ? i
                : default;
            if (arr.ValueKind != JsonValueKind.Array) return new();
            var list = new List<OtaVehicleRow>();
            foreach (var el in arr.EnumerateArray())
            {
                var id = GetStr(el, "agv_id", "id", "carId") ?? "";
                var name = GetStr(el, "agv_name", "name") ?? id;
                var ip = GetStr(el, "agv_ip", "ip");
                var state = GetStr(el, "status", "state");
                if (string.IsNullOrEmpty(id) && string.IsNullOrEmpty(ip)) continue;
                list.Add(new OtaVehicleRow
                {
                    Id = string.IsNullOrEmpty(id) ? ip! : id,
                    Name = name,
                    Ip = ip,
                    State = state
                });
            }
            return list;
        }
        catch (Exception ex)
        {
            _log.LogDebug(ex, "agv/list failed");
            return new();
        }
    }

    private static List<OtaVehicleRow> ParseCars(string text)
    {
        using var doc = JsonDocument.Parse(text);
        var root = doc.RootElement;
        var arr = root.ValueKind == JsonValueKind.Array ? root
            : root.TryGetProperty("cars", out var c) ? c
            : root.TryGetProperty("items", out var i) ? i
            : root.TryGetProperty("data", out var d) ? d
            : default;
        if (arr.ValueKind != JsonValueKind.Array) return new();
        var list = new List<OtaVehicleRow>();
        foreach (var el in arr.EnumerateArray())
        {
            var id = GetStr(el, "id", "carId", "rawId") ?? "";
            var name = GetStr(el, "name") ?? id;
            var ip = GetStr(el, "ip");
            var state = GetStr(el, "state", "status");
            var group = GetStr(el, "group");
            if (string.IsNullOrEmpty(id)) continue;
            list.Add(new OtaVehicleRow { Id = id, Name = name, Ip = ip, State = state, Group = group });
        }
        return list;
    }

    private static string? GetStr(JsonElement el, params string[] names)
    {
        foreach (var n in names)
        {
            if (el.TryGetProperty(n, out var p) && p.ValueKind == JsonValueKind.String)
                return p.GetString();
            if (el.TryGetProperty(n, out p) && p.ValueKind is JsonValueKind.Number)
                return p.ToString();
        }
        return null;
    }
}

/// <summary>避免 Ota 层直接依赖 Auth 命名空间循环；薄包装 InternalTokenStore。</summary>
public sealed class InternalTokenStoreAccessor
{
    private readonly Auth.InternalTokenStore _store;
    public InternalTokenStoreAccessor(Auth.InternalTokenStore store) => _store = store;
    public string Token => _store.Token;
}
