using RolemasterDb.ImportTool.Parsing;

namespace RolemasterDb.ImportTool.Tests;

public sealed class CriticalImportArtifactGenerationIntegrationTests
{
    private static readonly PdfXmlExtractor Extractor = new();
    private static readonly StandardCriticalTableParser StandardParser = new();
    private static readonly StandardOcrBootstrapper StandardOcrBootstrapper = new();

    [Fact]
    public async Task Generated_artifacts_include_page_and_cell_source_images()
    {
        var (parseResult, artifactPaths) = await LoadPreparedSlashParseResultAsync();
        var result = FindResult(parseResult, "71-75", "A");
        var cellArtifact = parseResult.Cells.Single(item =>
            item.GroupKey is null &&
            item.RollBandLabel == "71-75" &&
            item.ColumnKey == "A");

        Assert.True(result.SourceBounds.PageNumber > 0);
        Assert.True(result.SourceBounds.Width > 0);
        Assert.True(result.SourceBounds.Height > 0);
        Assert.NotNull(result.SourceImagePath);
        Assert.NotNull(result.SourceImageCrop);
        Assert.Equal(PdfXmlExtractor.RenderScaleFactor, result.SourceImageCrop!.ScaleFactor);
        Assert.Equal(PdfXmlExtractor.ScaledRenderDpi, result.SourceImageCrop.RenderDpi);
        Assert.Equal(result.SourceBounds.Width * PdfXmlExtractor.RenderScaleFactor, result.SourceImageCrop.BoundsWidth);
        Assert.Equal(result.SourceBounds.Height * PdfXmlExtractor.RenderScaleFactor, result.SourceImageCrop.BoundsHeight);
        Assert.Equal(result.SourceImagePath, cellArtifact.SourceImagePath);
        Assert.NotNull(cellArtifact.SourceImageCrop);
        Assert.True(File.Exists(artifactPaths.GetPageImagePath(result.SourceBounds.PageNumber)));
        Assert.True(File.Exists(artifactPaths.ResolveRelativePath(result.SourceImagePath!)));
    }

    [Fact]
    public async Task Generated_ocr_artifacts_preserve_pixel_space_crop_metadata()
    {
        var (parseResult, artifactPaths) = await LoadPreparedVoidParseResultAsync();
        var result = FindResult(parseResult, "96-99", "D");
        var cellArtifact = parseResult.Cells.Single(item =>
            item.GroupKey is null &&
            item.RollBandLabel == "96-99" &&
            item.ColumnKey == "D");

        Assert.True(result.SourceBounds.PageNumber > 0);
        Assert.True(result.SourceBounds.Width > 0);
        Assert.True(result.SourceBounds.Height > 0);

        Assert.NotNull(result.SourceImagePath);
        Assert.NotNull(result.SourceImageCrop);
        Assert.Equal(1, result.SourceImageCrop!.ScaleFactor);
        Assert.Equal(PdfXmlExtractor.ScaledRenderDpi, result.SourceImageCrop.RenderDpi);
        Assert.Equal(3600, result.SourceImageCrop.PageWidth);
        Assert.Equal(5070, result.SourceImageCrop.PageHeight);
        Assert.Equal(result.SourceBounds.Width, result.SourceImageCrop.BoundsWidth);
        Assert.Equal(result.SourceBounds.Height, result.SourceImageCrop.BoundsHeight);
        Assert.Equal(result.SourceImagePath, cellArtifact.SourceImagePath);
        Assert.NotNull(cellArtifact.SourceImageCrop);
        Assert.True(File.Exists(artifactPaths.GetPageImagePath(result.SourceBounds.PageNumber)));
        Assert.True(File.Exists(artifactPaths.ResolveRelativePath(result.SourceImagePath!)));
    }

    private static async Task<(CriticalTableParseResult ParseResult, ImportArtifactPaths ArtifactPaths)> LoadPreparedSlashParseResultAsync()
    {
        var entry = LoadManifest().Tables.Single(item => item.Slug == "slash");
        var xmlPath = Path.Combine(GetArtifactCacheRoot(), $"{entry.Slug}.xml");

        if (!File.Exists(xmlPath))
        {
            await Extractor.ExtractAsync(Path.Combine(GetRepositoryRoot(), entry.PdfPath), xmlPath);
        }

        var parseResult = StandardParser.Parse(entry, await File.ReadAllTextAsync(xmlPath));
        var artifactRoot = Path.Combine(GetArtifactCacheRoot(), Guid.NewGuid().ToString("N"));
        var artifactPaths = ImportArtifactPaths.Create(artifactRoot, entry.Slug);
        var generator = new CriticalSourceImageArtifactGenerator(new PdfXmlExtractor());

        await generator.GenerateAsync(Path.Combine(GetRepositoryRoot(), entry.PdfPath), artifactPaths, parseResult);
        return (parseResult, artifactPaths);
    }

    private static async Task<(CriticalTableParseResult ParseResult, ImportArtifactPaths ArtifactPaths)> LoadPreparedVoidParseResultAsync()
    {
        var entry = LoadManifest().Tables.Single(item => item.Slug == "void");
        var source = new ExtractedCriticalSource(
            "ocr",
            "Imported from PDF OCR extraction.",
            SourceRenderProfile.OcrPixels(PdfXmlExtractor.ScaledRenderDpi),
            [new ParsedPdfPageGeometry(1, 3600, 5070)],
            OcrCriticalSourceExtractor.ParseTsv(await File.ReadAllTextAsync(GetVoidFixturePath())));
        var layout = StandardOcrBootstrapper.Bootstrap(source, StandardTableAxisTemplateCatalog.Resolve(entry.AxisTemplateSlug));
        var parseResult = StandardParser.Parse(entry, source, layout);
        var artifactRoot = Path.Combine(GetArtifactCacheRoot(), Guid.NewGuid().ToString("N"));
        var artifactPaths = ImportArtifactPaths.Create(artifactRoot, entry.Slug);
        var generator = new CriticalSourceImageArtifactGenerator(new PdfXmlExtractor());

        await generator.GenerateAsync(Path.Combine(GetRepositoryRoot(), entry.PdfPath), artifactPaths, parseResult);
        return (parseResult, artifactPaths);
    }

    private static ParsedCriticalResult FindResult(CriticalTableParseResult parseResult, string rollBandLabel, string columnKey) =>
        parseResult.Table.Results.Single(item =>
            item.GroupKey is null &&
            item.RollBandLabel == rollBandLabel &&
            item.ColumnKey == columnKey);

    private static CriticalImportManifest LoadManifest() =>
        new CriticalImportManifestLoader().Load(Path.Combine(GetRepositoryRoot(), "sources", "critical-import-manifest.json"));

    private static string GetVoidFixturePath() =>
        Path.Combine(GetRepositoryRoot(), "src", "RolemasterDb.ImportTool.Tests", "Fixtures", "Void", "source.ocr.tsv");

    private static string GetArtifactCacheRoot()
    {
        var cacheRoot = Path.Combine(Path.GetTempPath(), "RolemasterDb.ImportTool.MergeTests");
        Directory.CreateDirectory(cacheRoot);
        return cacheRoot;
    }

    private static string GetRepositoryRoot() => TestRepositoryPaths.GetRepositoryRoot();
}
