using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.RegularExpressions;
using GerbilManagerWebAPI.Models;
using Microsoft.Extensions.DependencyInjection;

namespace GerbilManager.Tests;

/// <summary>profilePhotoUrl: computed field on GerbilDto — first photo by sortOrder, or null.</summary>
public class GerbilProfilePhotoTests : IClassFixture<ApiFactory>
{
    private readonly HttpClient _client;
    private readonly ApiFactory _factory;

    public GerbilProfilePhotoTests(ApiFactory factory)
    {
        _factory = factory;
        _client = factory.CreateClient();
    }

    private static Guid ExtractId(string json) =>
        Guid.Parse(Regex.Match(json, "\"id\":\"([^\"]+)\"").Groups[1].Value);

    private static string? GetStr(JsonElement el, string prop) =>
        el.TryGetProperty(prop, out var v) && v.ValueKind == JsonValueKind.String
            ? v.GetString() : null;

    private async Task<Guid> CreateGerbil(string name)
    {
        var resp = await _client.PostAsync("/gerbils", JsonContent.Create(new { name, gender = "female" }));
        Assert.Equal(HttpStatusCode.Created, resp.StatusCode);
        return ExtractId(await resp.Content.ReadAsStringAsync());
    }

    private void InsertPhotos(Guid gerbilId, params (string fileName, int sortOrder)[] photos)
    {
        using var scope = _factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
        foreach (var (fn, so) in photos)
            db.GerbilPhotos.Add(new GerbilPhoto
            {
                Id = Guid.NewGuid(), GerbilId = gerbilId,
                FileName = fn, SortOrder = so, CreatedAt = DateTimeOffset.UtcNow,
            });
        db.SaveChanges();
    }

    [Fact]
    public async Task GetById_without_photos_returns_null_profilePhotoUrl()
    {
        var id = await CreateGerbil("NoPhoto");
        var json = await _client.GetStringAsync($"/gerbils/{id}");
        var el = JsonDocument.Parse(json).RootElement;
        Assert.True(el.TryGetProperty("profilePhotoUrl", out var v));
        Assert.Equal(JsonValueKind.Null, v.ValueKind);
    }

    [Fact]
    public async Task GetById_with_photos_returns_lowest_sortOrder_url()
    {
        var id = await CreateGerbil("PhotoGerbil");
        InsertPhotos(id, ("second.jpg", 1), ("first.jpg", 0));

        var json = await _client.GetStringAsync($"/gerbils/{id}");
        var el = JsonDocument.Parse(json).RootElement;
        Assert.Equal("/photos/files/first.jpg", GetStr(el, "profilePhotoUrl"));
    }

    [Fact]
    public async Task GetList_returns_profilePhotoUrl_without_n_plus_1()
    {
        var id = await CreateGerbil("ListPhoto");
        InsertPhotos(id, ("list-photo.jpg", 0));

        var json = await _client.GetStringAsync("/gerbils");
        var root = JsonDocument.Parse(json).RootElement;
        var items = root.GetProperty("items").EnumerateArray().ToList();
        var gerbil = items.FirstOrDefault(x => GetStr(x, "id") == id.ToString());
        Assert.NotEqual(default, gerbil);
        Assert.Equal("/photos/files/list-photo.jpg", GetStr(gerbil, "profilePhotoUrl"));
    }
}
