using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using GerbilManagerWebAPI.Import;
using GerbilManagerWebAPI.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace GerbilManager.Tests;
///
/// EXHIBITION: show/exhibition results & awards per animal (/exhibitions).
/// - POST creates a row; GET (optionally filtered by gerbilId) returns it, newest first.
/// - PUT edits, DELETE removes; unknown ids return 404; an empty EventName is rejected (400).
/// - CRITICAL: exhibition rows survive the import re-ingest wipe (loose, FK-free GerbilId).
///
public class ExhibitionEndpointTests : IClassFixture
{
private readonly ApiFactory _factory;
public ExhibitionEndpointTests(ApiFactory factory) => _factory = factory;
[Fact]
public async Task Post_creates_and_get_filters_by_gerbilId()
{
var client = _factory.CreateClient();
var gerbilId = Guid.NewGuid();
var resp = await client.PostAsJsonAsync("/exhibitions", new
{
gerbilId,
entityName = "Krümel",
eventName = "Nationale Rennmausschau 2026",
date = "2026-03-15T00:00:00Z",
placement = "1. Platz",
award = "Best in Show",
note = "Sehr ausgeglichenes Tier.",
});
Assert.Equal(HttpStatusCode.Created, resp.StatusCode);
var created = JsonDocument.Parse(await resp.Content.ReadAsStringAsync()).RootElement;
var id = created.GetProperty("id").GetString();
Assert.False(string.IsNullOrEmpty(id));
Assert.Equal("Nationale Rennmausschau 2026", created.GetProperty("eventName").GetString());
Assert.Equal("1. Platz", created.GetProperty("placement").GetString());
Assert.Equal("Best in Show", created.GetProperty("award").GetString());
Assert.Equal(gerbilId.ToString(), created.GetProperty("gerbilId").GetString());
// GET filtered to this gerbil returns the row.
var listed = JsonDocument.Parse(await client.GetStringAsync($"/exhibitions?gerbilId={gerbilId}")).RootElement;
Assert.Contains(listed.EnumerateArray(),
x => x.GetProperty("id").GetString() == id
&& x.GetProperty("eventName").GetString() == "Nationale Rennmausschau 2026");
// GET filtered to a DIFFERENT gerbil does not.
var other = JsonDocument.Parse(await client.GetStringAsync($"/exhibitions?gerbilId={Guid.NewGuid()}")).RootElement;
Assert.DoesNotContain(other.EnumerateArray(), x => x.GetProperty("id").GetString() == id);
}
[Fact]
public async Task Crud_lifecycle_edit_and_delete()
{
var client = _factory.CreateClient();
var create = await client.PostAsJsonAsync("/exhibitions", new
{
eventName = "Lokalschau",
entityName = "Balu",
});
Assert.Equal(HttpStatusCode.Created, create.StatusCode);
var id = JsonDocument.Parse(await create.Content.ReadAsStringAsync()).RootElement.GetProperty("id").GetString();
// Edit: change placement + add a date.
var edit = await client.PutAsJsonAsync($"/exhibitions/{id}", new
{
placement = "2. Platz",
date = "2025-11-01T00:00:00Z",
});
Assert.Equal(HttpStatusCode.OK, edit.StatusCode);
var edited = JsonDocument.Parse(await edit.Content.ReadAsStringAsync()).RootElement;
Assert.Equal("2. Platz", edited.GetProperty("placement").GetString());
Assert.Equal("Lokalschau", edited.GetProperty("eventName").GetString());
// Delete -> 204, then 404 on subsequent edit/delete.
var del = await client.DeleteAsync($"/exhibitions/{id}");
Assert.Equal(HttpStatusCode.NoContent, del.StatusCode);
Assert.Equal(HttpStatusCode.NotFound, (await client.DeleteAsync($"/exhibitions/{id}")).StatusCode);
Assert.Equal(HttpStatusCode.NotFound, (await client.PutAsJsonAsync($"/exhibitions/{id}", new { eventName = "x" })).StatusCode);
}
[Fact]
public async Task Post_rejects_empty_event_name()
{
var client = _factory.CreateClient();
var resp = await client.PostAsJsonAsync("/exhibitions", new { eventName = " " });
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
}
[Fact]
public async Task Put_and_delete_unknown_id_return_404()
{
var client = _factory.CreateClient();
var missing = Guid.NewGuid();
Assert.Equal(HttpStatusCode.NotFound, (await client.PutAsJsonAsync($"/exhibitions/{missing}", new { eventName = "x" })).StatusCode);
Assert.Equal(HttpStatusCode.NotFound, (await client.DeleteAsync($"/exhibitions/{missing}")).StatusCode);
}
[Fact]
public async Task Exhibition_survives_ingest_wipe()
{
// Fresh in-memory DB seeded with a resolved import file (mirrors IngestResolvedServiceTests).
var dir = Path.Combine(Path.GetTempPath(), "exhibition-ingest-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(dir);
try
{
var contactId = Guid.NewGuid();
var fatherId = Guid.NewGuid();
var motherId = Guid.NewGuid();
var litterId = Guid.NewGuid();
var data = new
{
Contacts = new[]
{
new { Id = contactId, Name = "Test Breeder", Email = "t@e.de", Phone = "", Address = "", Notes = (string?)null, IsBreeder = true, IsReceiver = false, NameSuffix = (string?)null, Provenance = (string?)null }
},
Litters = new[]
{
new { Id = litterId, Name = "Wurf A", Date = "2026-01-01", TotalBorn = 5, DeathsWithin8Weeks = 0, FatherId = fatherId, MotherId = motherId, ExpectedGoHomeDate = (string?)null, Notes = "", PairingCode = "PC01", ExternalRef = "ext-litter-1", LitterLetter = "A" }
},
Gerbils = new[]
{
Animal(fatherId, "Papa", "male", contactId),
Animal(motherId, "Mama", "female", contactId),
},
GerbilPhotos = Array.Empty