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

namespace GerbilManager.Tests;

/// <summary>
/// REFS: POST /refs/resolve löst getippte Kurz-Verweise (tier|kontakt|wurf|gehege) auf.
///  - exakter (volle GUID) und kurzer (Präfix) Treffer je Typ
///  - mehrdeutiges Präfix (mehrere Treffer) => id=null (nicht geraten)
///  - unbekannter Code / unbekannter Typ => id=null
/// Entitäten werden mit deterministischen GUIDs direkt in die SQLite-Test-DB geseedet,
/// damit Präfixe und Mehrdeutigkeit kontrolliert getestet werden können.
/// </summary>
public class RefsEndpointTests : IClassFixture<ApiFactory>
{
    private readonly ApiFactory _factory;
    public RefsEndpointTests(ApiFactory factory) => _factory = factory;

    // Deterministische GUIDs: zwei Tiere mit GEMEINSAMEM 6-Hex-Präfix "abcdef" (mehrdeutig),
    // sowie je eine eindeutige Entität pro Typ.
    private static readonly Guid TierA = new("abcdef00-0000-0000-0000-000000000001");
    private static readonly Guid TierB = new("abcdef00-0000-0000-0000-000000000002");
    private static readonly Guid Kontakt = new("11112222-3333-4444-5555-666677778888");
    private static readonly Guid Wurf = new("99990000-aaaa-bbbb-cccc-ddddeeeeffff");
    private static readonly Guid Gehege = new("12345678-9abc-def0-1234-567890abcdef");

    private async Task SeedAsync()
    {
        using var scope = _factory.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
        if (db.Gerbils.Any(g => g.Id == TierA)) return; // einmalig (geteilte Fixture)

        db.Gerbils.Add(new Gerbil { Id = TierA, Name = "Krümel", Gender = Gender.female });
        db.Gerbils.Add(new Gerbil { Id = TierB, Name = "Möhre", Gender = Gender.male });
        db.Contacts.Add(new Contact { Id = Kontakt, Name = "Erika Musterfrau" });
        db.Litters.Add(new Litter { Id = Wurf, Name = "Wurf X", Date = new DateOnly(2026, 1, 1) });
        db.Enclosures.Add(new Enclosure { Id = Gehege, Name = "Terrarium 1" });
        await db.SaveChangesAsync();
    }

    private async Task<List<JsonElement>> Resolve(params object[] refs)
    {
        var resp = await _factory.CreateClient()
            .PostAsJsonAsync("/refs/resolve", new { refs });
        resp.EnsureSuccessStatusCode();
        var doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync());
        return doc.RootElement.EnumerateArray().ToList();
    }

    [Fact]
    public async Task Resolves_full_guid_per_type()
    {
        await SeedAsync();
        var rows = await Resolve(
            new { type = "tier", code = TierA.ToString() },
            new { type = "kontakt", code = Kontakt.ToString() },
            new { type = "wurf", code = Wurf.ToString() },
            new { type = "gehege", code = Gehege.ToString() });

        Assert.Equal(TierA.ToString(), rows[0].GetProperty("id").GetString());
        Assert.Equal("Krümel", rows[0].GetProperty("name").GetString());
        Assert.Equal(Kontakt.ToString(), rows[1].GetProperty("id").GetString());
        Assert.Equal("Erika Musterfrau", rows[1].GetProperty("name").GetString());
        Assert.Equal(Wurf.ToString(), rows[2].GetProperty("id").GetString());
        Assert.Equal("Wurf X", rows[2].GetProperty("name").GetString());
        Assert.Equal(Gehege.ToString(), rows[3].GetProperty("id").GetString());
        Assert.Equal("Terrarium 1", rows[3].GetProperty("name").GetString());
    }

    [Fact]
    public async Task Resolves_short_prefix_when_unique()
    {
        await SeedAsync();
        // 8-Hex-Kurz-ID des Kontakts ("11112222") ist eindeutig.
        var rows = await Resolve(new { type = "kontakt", code = "11112222" });
        Assert.Equal(Kontakt.ToString(), rows[0].GetProperty("id").GetString());
        Assert.Equal("Erika Musterfrau", rows[0].GetProperty("name").GetString());
    }

    [Fact]
    public async Task Tolerates_dashes_and_case_in_code()
    {
        await SeedAsync();
        // Gemischte Schreibweise + Bindestriche im Kurz-Code des Geheges.
        var rows = await Resolve(new { type = "gehege", code = "1234-5678" });
        Assert.Equal(Gehege.ToString(), rows[0].GetProperty("id").GetString());
        Assert.Equal("Terrarium 1", rows[0].GetProperty("name").GetString());
    }

    [Fact]
    public async Task Ambiguous_prefix_resolves_to_null()
    {
        await SeedAsync();
        // "abcdef" trifft TierA UND TierB => mehrdeutig => id/name null.
        var rows = await Resolve(new { type = "tier", code = "abcdef" });
        Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("id").ValueKind);
        Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("name").ValueKind);
        Assert.Equal("tier", rows[0].GetProperty("type").GetString());
        Assert.Equal("abcdef", rows[0].GetProperty("code").GetString());
    }

    [Fact]
    public async Task Unknown_code_and_unknown_type_resolve_to_null()
    {
        await SeedAsync();
        var rows = await Resolve(
            new { type = "tier", code = "fffffffe" },        // unbekannter Code
            new { type = "schubladen", code = "11112222" }); // unbekannter Typ
        Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("id").ValueKind);
        Assert.Equal(JsonValueKind.Null, rows[1].GetProperty("id").ValueKind);
    }

    [Fact]
    public async Task Too_short_code_resolves_to_null()
    {
        await SeedAsync();
        // < 6 Hex-Zeichen wird nicht aufgelöst (auch wenn es eindeutig wäre).
        var rows = await Resolve(new { type = "kontakt", code = "1111" });
        Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("id").ValueKind);
    }
}
