using System.Text.Json;
using GerbilManagerWebAPI.Import;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Xunit.Abstractions;

namespace GerbilManager.Tests
{
    /// <summary>
    /// Golden regression fixture: ingests the REAL <c>tools/import/output/resolved_import.json</c>
    /// (gitignored, local only) and asserts that every verified key animal's Akte still matches its
    /// frozen golden snapshot in <c>tools/import/verified-golden.json</c> (committed fixture).
    ///
    /// Both files are optional: if EITHER is missing the test skips cleanly (no failure), so it is a
    /// no-op on CI / fresh clones but a real guard on the breeder's machine once she has verified
    /// animals and exported the fixture.
    /// </summary>
    public class VerifiedGoldenTests
    {
        private readonly ITestOutputHelper _out;
        public VerifiedGoldenTests(ITestOutputHelper output) => _out = output;

        private sealed record GoldenEntry(Guid GerbilId, string? Name, DateTimeOffset? VerifiedAt, GerbilSnapshot? Snapshot);

        private static readonly JsonSerializerOptions Web = new(JsonSerializerDefaults.Web);

        private static string? FindRepoRoot()
        {
            var dir = new DirectoryInfo(AppContext.BaseDirectory);
            while (dir is not null)
            {
                if (Directory.Exists(Path.Combine(dir.FullName, "tools", "import")))
                    return dir.FullName;
                dir = dir.Parent;
            }
            return null;
        }

        [Fact]
        public async Task Verified_animals_still_match_their_golden_snapshot()
        {
            var repoRoot = FindRepoRoot();
            if (repoRoot is null)
            {
                _out.WriteLine("SKIP: repo root (tools/import) not found from the test host.");
                return;
            }

            var goldenPath = Path.Combine(repoRoot, "tools", "import", "verified-golden.json");
            var outputDir = Path.Combine(repoRoot, "tools", "import", "output");
            var resolvedPath = Path.Combine(outputDir, "resolved_import.json");

            if (!File.Exists(goldenPath))
            {
                _out.WriteLine($"SKIP: golden fixture not present ({goldenPath}).");
                return;
            }
            if (!File.Exists(resolvedPath))
            {
                _out.WriteLine($"SKIP: resolved_import.json not present ({resolvedPath}) — run the import pipeline.");
                return;
            }

            List<GoldenEntry>? golden;
            try
            {
                golden = JsonSerializer.Deserialize<List<GoldenEntry>>(await File.ReadAllTextAsync(goldenPath), Web);
            }
            catch (Exception ex)
            {
                _out.WriteLine($"SKIP: could not parse golden fixture: {ex.Message}");
                return;
            }
            if (golden is null || golden.Count == 0)
            {
                _out.WriteLine("SKIP: golden fixture is empty.");
                return;
            }

            // Ingest the real resolved_import.json into an InMemory DB.
            var opts = new DbContextOptionsBuilder<ApplicationContext>()
                .UseInMemoryDatabase("verified-golden-" + Guid.NewGuid().ToString("N"))
                .Options;
            using var db = new ApplicationContext(opts);
            db.Database.EnsureCreated();

            var photoDir = Path.Combine(Path.GetTempPath(), "verified-golden-photos-" + Guid.NewGuid().ToString("N"));
            var config = new ConfigurationBuilder()
                .AddInMemoryCollection(new Dictionary<string, string?>
                {
                    { "Import:SourcePath", outputDir },
                    { "Photos:RootPath", photoDir },
                })
                .Build();

            try
            {
                var result = await new IngestResolvedService(db, config, null!).RunAsync();
                Assert.StartsWith("Ingestion successful!", result);
            }
            catch (Exception ex)
            {
                _out.WriteLine($"SKIP: ingest of the real resolved_import.json failed: {ex.Message}");
                return;
            }
            finally
            {
                try { Directory.Delete(photoDir, recursive: true); } catch { }
            }

            var mismatches = new List<string>();
            int checkedCount = 0;
            foreach (var entry in golden)
            {
                if (entry.Snapshot is null) continue;
                checkedCount++;
                GerbilSnapshot? actual;
                try
                {
                    actual = await GerbilSnapshotService.BuildSnapshotAsync(db, entry.GerbilId);
                }
                catch (Exception ex)
                {
                    mismatches.Add($"• {entry.Name ?? entry.GerbilId.ToString()}: Akte konnte nicht gebaut werden — {ex.Message}");
                    continue;
                }

                if (actual is null)
                {
                    mismatches.Add($"• {entry.Name ?? entry.GerbilId.ToString()}: fehlt im Import (verifiziertes Tier verschwunden).");
                    continue;
                }

                var diffs = GerbilSnapshotService.Diff(entry.Snapshot, actual);
                if (diffs.Count > 0)
                {
                    var fields = string.Join("; ", diffs.Select(d => $"{d.Path}: golden='{d.GoldenValue}' ≠ import='{d.OtherValue}'"));
                    mismatches.Add($"• {entry.Name ?? entry.GerbilId.ToString()}: {diffs.Count} Abweichung(en) — {fields}");
                }
            }

            _out.WriteLine($"Golden-Fixture geprüft: {checkedCount} verifizierte Tiere, {mismatches.Count} abweichend.");
            if (mismatches.Count > 0)
            {
                var report = $"Golden-Regression fehlgeschlagen — {mismatches.Count} von {checkedCount} verifizierten Tieren weichen vom eingefrorenen Snapshot ab:\n"
                             + string.Join("\n", mismatches);
                Assert.Fail(report);
            }
        }
    }
}
