using System.Net;
using System.Net.Http;
using ClaudeDo.Releases;

namespace ClaudeDo.Releases.Tests;

public sealed class ReleaseClientTests
{
    private const string ApiBase = "https://git.example.test/api/v1/repos/releases/ClaudeDo";

    [Fact]
    public async Task GetLatestReleaseAsync_ParsesTagAndAssets()
    {
        const string json = """
            {
              "tag_name": "v0.2.0",
              "name": "v0.2.0",
              "assets": [
                {
                  "name": "ClaudeDo-0.2.0-win-x64.zip",
                  "browser_download_url": "https://git.example.test/dl/zip",
                  "size": 12345
                },
                {
                  "name": "checksums.txt",
                  "browser_download_url": "https://git.example.test/dl/checksums",
                  "size": 128
                }
              ]
            }
            """;

        var handler = new FakeHttpMessageHandler(HttpStatusCode.OK, json);
        using var http = new HttpClient(handler);
        var client = new ReleaseClient(http, ApiBase);

        var release = await client.GetLatestReleaseAsync(CancellationToken.None);

        Assert.NotNull(release);
        Assert.Equal("v0.2.0", release!.TagName);
        Assert.Equal(2, release.Assets.Count);
        Assert.Equal("ClaudeDo-0.2.0-win-x64.zip", release.Assets[0].Name);
        Assert.Equal("https://git.example.test/dl/zip", release.Assets[0].BrowserDownloadUrl);
        Assert.Equal(12345, release.Assets[0].Size);
    }

    [Fact]
    public async Task GetLatestReleaseAsync_Returns_Null_On404()
    {
        var handler = new FakeHttpMessageHandler(HttpStatusCode.NotFound, "");
        using var http = new HttpClient(handler);
        var client = new ReleaseClient(http, ApiBase);

        var release = await client.GetLatestReleaseAsync(CancellationToken.None);

        Assert.Null(release);
    }

    [Fact]
    public async Task GetLatestReleaseAsync_Returns_Null_OnNetworkError()
    {
        var handler = new FakeHttpMessageHandler(_ => throw new HttpRequestException("boom"));
        using var http = new HttpClient(handler);
        var client = new ReleaseClient(http, ApiBase);

        var release = await client.GetLatestReleaseAsync(CancellationToken.None);

        Assert.Null(release);
    }

    [Fact]
    public async Task GetLatestReleaseAsync_Hits_CorrectUrl()
    {
        var handler = new FakeHttpMessageHandler(HttpStatusCode.OK, "{\"tag_name\":\"v0.1.0\",\"assets\":[]}");
        using var http = new HttpClient(handler);
        var client = new ReleaseClient(http, ApiBase);

        _ = await client.GetLatestReleaseAsync(CancellationToken.None);

        Assert.Single(handler.Requests);
        Assert.Equal($"{ApiBase}/releases/latest", handler.Requests[0].RequestUri!.ToString());
    }

    [Fact]
    public async Task DownloadAsync_WritesBytesToDisk()
    {
        var payload = new byte[] { 1, 2, 3, 4, 5 };
        var handler = new FakeHttpMessageHandler(_ => new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new ByteArrayContent(payload)
        });
        using var http = new HttpClient(handler);
        var client = new ReleaseClient(http, ApiBase);

        var tempPath = Path.Combine(Path.GetTempPath(), "ClaudeDoDlTest-" + Guid.NewGuid().ToString("N"));
        try
        {
            await client.DownloadAsync("https://example/foo", tempPath,
                new Progress<long>(_ => { }), CancellationToken.None);

            Assert.True(File.Exists(tempPath));
            Assert.Equal(payload, File.ReadAllBytes(tempPath));
        }
        finally
        {
            if (File.Exists(tempPath)) File.Delete(tempPath);
        }
    }
}
