using System.Net;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YMhut.Box.Core.App;
using YMhut.Box.Core.Net;
using YMhut.Box.Core.SolarSystem;

namespace YMhut.Box.Tests;

[TestClass]
public sealed class SolarEphemerisServiceTests
{
    [TestMethod]
    public void MiriadeParserReadsRectangularPxPyPzPayload()
    {
        var json = """
            {
              "data": [
                {
                  "Date": 0.24611925E+7,
                  "px": -0.32062933285171752E+0,
                  "py": 0.14714396284300654E+0,
                  "pz": 0.41432383444950593E-1
                }
              ]
            }
            """;

        var parsed = SolarEphemerisService.TryParseMiriadePayload(json, out var vector);

        Assert.IsTrue(parsed);
        Assert.AreEqual(-0.32062933285171752, vector.X, 1e-12);
        Assert.AreEqual(0.14714396284300654, vector.Y, 1e-12);
        Assert.AreEqual(0.041432383445, vector.Z, 1e-12);
    }

    [TestMethod]
    public async Task CurrentEphemerisReturnsApproximatePayloadWhenPreciseCacheMissing()
    {
        var root = Path.Combine(Path.GetTempPath(), "ymhut-solar-tests", Guid.NewGuid().ToString("N"));
        try
        {
            var service = new SolarEphemerisService(
                AppPaths.ForCurrentUser(root),
                new FakeHttpService(_ => Task.FromException<string>(new InvalidOperationException())));

            var payload = await service.GetCurrentAsync();

            Assert.AreEqual("ephemeris:sync", payload.Type);
            Assert.AreEqual("China Ephemeris Approx", payload.Source);
            Assert.HasCount(8, payload.Planets);
        }
        finally
        {
            if (Directory.Exists(root))
            {
                Directory.Delete(root, recursive: true);
            }
        }
    }

    [TestMethod]
    public async Task PreciseEphemerisRequestsMiriadePlanetsConcurrently()
    {
        var root = Path.Combine(Path.GetTempPath(), "ymhut-solar-tests", Guid.NewGuid().ToString("N"));
        var requestTimes = new List<DateTimeOffset>();
        try
        {
            var service = new SolarEphemerisService(
                AppPaths.ForCurrentUser(root),
                new FakeHttpService(async uri =>
                {
                    lock (requestTimes)
                    {
                        requestTimes.Add(DateTimeOffset.UtcNow);
                    }

                    await Task.Delay(80);
                    if (uri.Host.Contains("ssp.imcce.fr", StringComparison.OrdinalIgnoreCase))
                    {
                        return """
                            {
                              "data": [
                                { "px": 1.0, "py": 2.0, "pz": 3.0 }
                              ]
                            }
                            """;
                    }

                    throw new InvalidOperationException("Unexpected endpoint");
                }));

            var payload = await service.GetPreciseCurrentAsync();

            Assert.IsNotNull(payload);
            Assert.AreEqual("IMCCE Miriade", payload.Source);
            Assert.HasCount(8, payload.Planets);
            Assert.HasCount(8, requestTimes);
            Assert.IsLessThan(70, (requestTimes.Max() - requestTimes.Min()).TotalMilliseconds);
        }
        finally
        {
            if (Directory.Exists(root))
            {
                Directory.Delete(root, recursive: true);
            }
        }
    }

    private sealed class FakeHttpService(Func<Uri, Task<string>> handler) : IHttpService
    {
        public async Task<HttpServiceResult> GetAsync(Uri uri, CancellationToken cancellationToken = default)
        {
            var content = await GetStringAsync(uri, cancellationToken);
            return new HttpServiceResult(HttpStatusCode.OK, content, new Dictionary<string, string[]>(), TimeSpan.FromMilliseconds(1));
        }

        public Task<string> GetStringAsync(Uri uri, CancellationToken cancellationToken = default)
        {
            return handler(uri);
        }

        public Task<HttpServiceResult> SendAsync(
            Uri uri,
            string method = "GET",
            string? body = null,
            IReadOnlyDictionary<string, string>? headers = null,
            bool ensureSuccess = true,
            HttpRequestPolicy? policy = null,
            CancellationToken cancellationToken = default)
        {
            return GetAsync(uri, cancellationToken);
        }
    }
}
