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;

/// <summary>
/// RÜCKNAHMEN (getback_tb): zurückgekommene/zurückgenommene Tiere.
///  - Voller CRUD-Round-Trip (POST/GET[?gerbilId=]/PUT/DELETE).
///  - Validierung: ohne Tier (weder GerbilId noch GerbilName) → 400.
///  - CRITICAL: Rücknahme-Zeilen überleben den Import-Re-Ingest-Wipe
///    (lose, FK-freie GerbilId/FromContactId — wie Feedback).
/// </summary>
public class ReturnRecordEndpointTests : IClassFixture<ApiFactory>
{
    private readonly ApiFactory _factory;
    public ReturnRecordEndpointTests(ApiFactory factory) => _factory = factory;

    [Fact]
    public async Task Crud_round_trip_create_list_update_delete()
    {
        var client = _factory.CreateClient();

        var gerbilId = Guid.NewGuid();
        var contactId = Guid.NewGuid();

        // POST
        var resp = await client.PostAsJsonAsync("/returns", new
        {
            gerbilId,
            gerbilName = "Krümel",
            returnDate = "2026-05-01T00:00:00Z",
            returnPrice = 0m,
            originalPrice = 15m,
            originalSaleDate = "2025-09-01T00:00:00Z",
            fromContactId = contactId,
            fromContactName = "Familie Meier",
            note = "Allergie in der Familie",
        });
        Assert.Equal(HttpStatusCode.Created, resp.StatusCode);
        var created = JsonDocument.Parse(await resp.Content.ReadAsStringAsync()).RootElement;
        var id = created.GetProperty("id").GetString()!;
        Assert.Equal("Krümel", created.GetProperty("gerbilName").GetString());
        Assert.Equal(gerbilId.ToString(), created.GetProperty("gerbilId").GetString());
        Assert.Equal("Familie Meier", created.GetProperty("fromContactName").GetString());

        // GET (unfiltered) returns it
        var listed = JsonDocument.Parse(await client.GetStringAsync("/returns")).RootElement;
        Assert.Contains(listed.EnumerateArray(), r => r.GetProperty("id").GetString() == id);

        // GET ?gerbilId= filters
        var filtered = JsonDocument.Parse(
            await client.GetStringAsync($"/returns?gerbilId={gerbilId}")).RootElement;
        Assert.All(filtered.EnumerateArray(),
            r => Assert.Equal(gerbilId.ToString(), r.GetProperty("gerbilId").GetString()));
        Assert.Contains(filtered.EnumerateArray(), r => r.GetProperty("id").GetString() == id);

        var other = JsonDocument.Parse(
            await client.GetStringAsync($"/returns?gerbilId={Guid.NewGuid()}")).RootElement;
        Assert.Empty(other.EnumerateArray());

        // PUT updates
        var putResp = await client.PutAsJsonAsync($"/returns/{id}", new
        {
            gerbilId,
            gerbilName = "Krümel",
            returnDate = "2026-05-02T00:00:00Z",
            returnPrice = 5m,
            note = "Korrigierter Grund",
        });
        Assert.Equal(HttpStatusCode.OK, putResp.StatusCode);
        var updated = JsonDocument.Parse(await putResp.Content.ReadAsStringAsync()).RootElement;
        Assert.Equal("Korrigierter Grund", updated.GetProperty("note").GetString());
        Assert.Equal(5m, updated.GetProperty("returnPrice").GetDecimal());

        // DELETE removes
        var delResp = await client.DeleteAsync($"/returns/{id}");
        Assert.Equal(HttpStatusCode.NoContent, delResp.StatusCode);
        var afterDelete = JsonDocument.Parse(await client.GetStringAsync("/returns")).RootElement;
        Assert.DoesNotContain(afterDelete.EnumerateArray(), r => r.GetProperty("id").GetString() == id);

        // DELETE again → 404
        var delAgain = await client.DeleteAsync($"/returns/{id}");
        Assert.Equal(HttpStatusCode.NotFound, delAgain.StatusCode);
    }

    [Fact]
    public async Task Post_rejects_record_without_animal()
    {
        var client = _factory.CreateClient();
        var resp = await client.PostAsJsonAsync("/returns", new { note = "irgendwas" });
        Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
    }

    [Fact]
    public async Task Put_unknown_id_returns_404()
    {
        var client = _factory.CreateClient();
        var resp = await client.PutAsJsonAsync($"/returns/{Guid.NewGuid()}", new { gerbilName = "X" });
        Assert.Equal(HttpStatusCode.NotFound, resp.StatusCode);
    }

    [Fact]
    public async Task ReturnRecord_survives_ingest_wipe()
    {
        var dir = Path.Combine(Path.GetTempPath(), "returns-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<object>(),
            };
            File.WriteAllText(Path.Combine(dir, "resolved_import.json"), JsonSerializer.Serialize(data));

            var opts = new DbContextOptionsBuilder<ApplicationContext>()
                .UseInMemoryDatabase("returns-ingest-" + Guid.NewGuid().ToString("N"))
                .Options;
            using var db = new ApplicationContext(opts);
            db.Database.EnsureCreated();

            // A return record referencing the gerbil + contact that the wipe will delete.
            var recordId = Guid.NewGuid();
            db.ReturnRecords.Add(new ReturnRecord
            {
                Id = recordId,
                GerbilId = fatherId,
                GerbilName = "Papa",
                ReturnDate = new DateTime(2026, 5, 1, 0, 0, 0, DateTimeKind.Utc),
                ReturnPrice = 0m,
                FromContactId = contactId,
                FromContactName = "Test Breeder",
                Note = "kam zurück",
                CreatedAt = DateTimeOffset.UtcNow,
            });
            await db.SaveChangesAsync();

            var config = new ConfigurationBuilder()
                .AddInMemoryCollection(new Dictionary<string, string?> { { "Import:SourcePath", dir } })
                .Build();

            var result = await new IngestResolvedService(db, config, null!).RunAsync();
            Assert.Contains("Ingestion successful!", result);

            // The gerbils/contacts were wiped & recreated, but the return record is untouched.
            var survivor = await db.ReturnRecords.SingleAsync(r => r.Id == recordId);
            Assert.Equal(fatherId, survivor.GerbilId);     // loose id preserved even though the gerbil row was deleted/recreated
            Assert.Equal(contactId, survivor.FromContactId);
            Assert.Equal("Papa", survivor.GerbilName);
            Assert.Equal("Test Breeder", survivor.FromContactName);
            Assert.Equal(1, await db.ReturnRecords.CountAsync());
        }
        finally
        {
            try { Directory.Delete(dir, recursive: true); } catch { /* best effort */ }
        }
    }

    private static object Animal(Guid id, string name, string gender, Guid contactId) => new
    {
        Id = id,
        Name = name,
        Gender = gender,
        Status = "Breeding",
        LitterId = (Guid?)null,
        OriginContactId = contactId,
        ReceiverContactId = (Guid?)null,
        EnclosureId = (Guid?)null,
        ColorVarietyId = new Guid("00000000-0000-0000-0000-000000000006"),
        DateOfBirth = "2025-01-01",
        DateOfDeath = (string?)null,
        CauseOfDeath = (string?)null,
        GoHomeDate = (string?)null,
        Genotype = "aa CC DD EE GG PP spsp rere",
        Notes = "",
        ImportSource = "docx-export",
        ExternalRef = "ext-" + name,
        RawImportData = "{}",
        OriginBreeder = "Test Zucht",
        NameSearch = name.ToLowerInvariant(),
        CharacterTraits = Array.Empty<string>(),
        CharacterNote = (string?)null,
        IsDeaf = false,
        IsResident = true,
    };
}
