using YMhut.Box.Core.Api;
using YMhut.Box.Core.App;
using YMhut.Box.Core.Media;

namespace YMhut.Box.Tests;

[TestClass]
public sealed class RemoteMediaCatalogTests
{
    [TestMethod]
    public void ParsesCurrentMediaTypesSnapshot()
    {
        var catalog = RemoteMediaCatalogParser.Parse(ReadRepoFile("server", "update", "public", "media-types.json"));

        Assert.AreEqual("1.0.6", catalog.LayoutVersion);
        Assert.AreEqual("grid", catalog.UiConfig.DefaultView);
        Assert.IsTrue(catalog.Categories.Count >= 2);

        var image = catalog.Categories.Single(category => category.Id == "image");
        Assert.IsTrue(image.Enabled);
        Assert.AreEqual(RemoteMediaKind.Image, image.Kind);
        Assert.IsTrue(image.Layout.ShowPreview);
        CollectionAssert.Contains(image.Sources.First(source => source.Id == "xjj").SupportedFormats.ToArray(), "jpg");
        Assert.AreEqual(30, image.Sources.First(source => source.Id == "xjj").RefreshIntervalSeconds);

        var video = catalog.Categories.Single(category => category.Id == "video");
        Assert.AreEqual(RemoteMediaKind.Video, video.Kind);
        Assert.IsFalse(video.Layout.AutoPlay);
        CollectionAssert.Contains(video.Sources.First().SupportedFormats.ToArray(), "mp4");
    }

    [TestMethod]
    public void ParsesLegacyMediaTypesSnapshot()
    {
        var catalog = RemoteMediaCatalogParser.Parse(ReadRepoFile("box-old", "server", "media-types.json"));

        Assert.AreEqual("1.0.8", catalog.LayoutVersion);
        Assert.IsTrue(catalog.Categories.Count >= 2);
        Assert.IsTrue(catalog.Categories.Any(category => category.Id == "image" && category.Sources.Count >= 7));
        Assert.IsTrue(catalog.Categories.Any(category => category.Id == "video" && category.Sources.Any(source => source.Id == "radom_xjj_mv")));
    }

    [TestMethod]
    public void AppliesDefaultsWhenOptionalFieldsAreMissing()
    {
        const string minimal = """
        {
          "categories": [
            {
              "id": "video",
              "subcategories": [
                {
                  "id": "demo",
                  "api_url": "https://example.test/media"
                }
              ]
            }
          ]
        }
        """;

        var catalog = RemoteMediaCatalogParser.Parse(minimal);
        var category = catalog.Categories.Single();
        var source = category.Sources.Single();

        Assert.IsTrue(category.Enabled);
        Assert.AreEqual(RemoteMediaKind.Video, category.Kind);
        Assert.AreEqual(1, category.Layout.Columns);
        Assert.AreEqual("16:9", category.Layout.AspectRatio);
        Assert.IsTrue(category.Layout.ShowPreview);
        Assert.IsFalse(category.Layout.AutoPlay);
        Assert.IsTrue(source.Downloadable);
        Assert.AreEqual(60, source.RefreshIntervalSeconds);
        CollectionAssert.AreEqual(new[] { "mp4", "webm" }, source.SupportedFormats.ToArray());
        Assert.AreEqual("https://example.test/media", source.ThumbnailUrl);
    }

    [TestMethod]
    public async Task ServiceWritesReadsFallsBackAndClearsCache()
    {
        var root = Path.Combine(Path.GetTempPath(), "ymhut-remote-media-" + Guid.NewGuid().ToString("N"));
        try
        {
            var paths = new AppPaths(root);
            paths.EnsureCreated();
            var content = ReadRepoFile("server", "update", "public", "media-types.json");
            var api = new FakeApiManager(content);
            var service = new RemoteMediaCatalogService(paths, api);

            var remote = await service.LoadAsync(forceRefresh: true);

            Assert.AreEqual(RemoteMediaCatalogLoadSource.Remote, remote.Source);
            Assert.IsTrue(api.LastUri?.Query.Contains("_=", StringComparison.Ordinal) == true);
            Assert.IsTrue(File.Exists(Path.Combine(paths.Cache, "remote-media", "media-types.json")));

            var fallback = new RemoteMediaCatalogService(paths, new FakeApiManager(string.Empty, success: false));
            var cached = await fallback.LoadAsync();

            Assert.AreEqual(RemoteMediaCatalogLoadSource.Cache, cached.Source);
            Assert.IsFalse(string.IsNullOrWhiteSpace(cached.Warning));

            await fallback.ClearCacheAsync();
            Assert.IsFalse(Directory.Exists(Path.Combine(paths.Cache, "remote-media")));
            Assert.IsTrue(Directory.Exists(paths.Cache));
        }
        finally
        {
            if (Directory.Exists(root))
            {
                Directory.Delete(root, recursive: true);
            }
        }
    }

    private static string ReadRepoFile(params string[] segments)
    {
        var directory = new DirectoryInfo(Directory.GetCurrentDirectory());
        while (directory is not null)
        {
            var candidate = Path.Combine(new[] { directory.FullName }.Concat(segments).ToArray());
            if (File.Exists(candidate))
            {
                return File.ReadAllText(candidate);
            }

            directory = directory.Parent;
        }

        throw new DirectoryNotFoundException("Unable to locate repository sample file.");
    }

    private sealed class FakeApiManager(string content, bool success = true) : IApiManager
    {
        public Uri? LastUri { get; private set; }

        public Task<ApiResponse> FetchAsync(string endpointId, string input = "", CancellationToken cancellationToken = default)
        {
            LastUri = RemoteMediaCatalogService.PrimaryConfigUri;
            return Task.FromResult(new ApiResponse(
                endpointId,
                LastUri,
                success,
                success ? content : string.Empty,
                success ? null : "offline",
                DateTimeOffset.Now,
                success ? 200 : 0));
        }

        public Task<ApiResponse> FetchUriAsync(string endpointId, Uri uri, string input = "", CancellationToken cancellationToken = default)
        {
            LastUri = uri;
            return Task.FromResult(new ApiResponse(
                endpointId,
                uri,
                success,
                success ? content : string.Empty,
                success ? null : "offline",
                DateTimeOffset.Now,
                success ? 200 : 0));
        }

        public Task<ApiHealthStatus> CheckHealthAsync(string endpointId, string input = "", CancellationToken cancellationToken = default)
        {
            return Task.FromResult(success ? ApiHealthStatus.Healthy : ApiHealthStatus.Unhealthy);
        }
    }
}
