namespace RolemasterDb.ImportTool.Parsing;

public sealed class StandardCriticalTableParser
{
    public CriticalTableParseResult Parse(CriticalImportManifestEntry entry, string xmlContent)
    {
        var fragments = CriticalTableParserSupport.LoadFragments(xmlContent);
        var pageGeometries = CriticalTableParserSupport.LoadPageGeometries(xmlContent);
        var headerFragments = FindHeaderFragments(fragments);
        var validationErrors = new List<string>();
        var validationWarnings = new List<string>();

        var columnCenters = headerFragments
            .OrderBy(item => item.Left)
            .Select(item => (Key: item.Text.ToUpperInvariant(), CenterX: item.CenterX))
            .ToList();

        var headerTop = headerFragments.Max(item => item.Top);
        var keyTop = CriticalTableParserSupport.FindKeyTop(fragments);
        var affixLegend = CriticalTableParserSupport.ParseAffixLegend(fragments, keyTop);
        var affixLegendSymbols = affixLegend.ClassificationSymbols;
        var leftCutoff = headerFragments.Min(item => item.Left) - 10;
        var rowLabelFragments = CriticalTableParserSupport.FindRowLabelFragments(
            fragments,
            leftCutoff,
            headerTop + CriticalTableParserSupport.HeaderToRowLabelMinimumGap,
            keyTop);
        var rowAnchors = CriticalTableParserSupport.CreateRowAnchors(rowLabelFragments);
        var bodyStartTop = CriticalTableParserSupport.ResolveBodyStartTop(headerTop, rowAnchors);

        if (rowAnchors.Count == 0)
        {
            validationErrors.Add("No roll-band labels were found in the XML artifact.");
        }

        var bodyFragments = CriticalTableParserSupport.BuildBodyFragments(
            fragments,
            bodyStartTop,
            keyTop,
            leftCutoff,
            rowAnchors,
            headerFragments,
            columnCenters,
            affixLegendSymbols);
        var bodyLines = CriticalTableParserSupport.BuildBodyLines(bodyFragments, columnCenters, affixLegendSymbols);

        var parsedRollBands = rowAnchors
            .Select(anchor => CriticalTableParserSupport.CreateRollBand(anchor.Label, anchor.SortOrder))
            .ToList();

        var cellEntries = new List<ColumnarCellEntry>();

        for (var rowIndex = 0; rowIndex < rowAnchors.Count; rowIndex++)
        {
            var rowStart = rowIndex == 0
                ? bodyStartTop
                : CriticalTableParserSupport.ResolveRowBoundaryTop(rowAnchors[rowIndex - 1], rowAnchors[rowIndex], bodyLines);

            var rowEnd = rowIndex == rowAnchors.Count - 1
                ? keyTop - 1
                : CriticalTableParserSupport.ResolveRowBoundaryTop(rowAnchors[rowIndex], rowAnchors[rowIndex + 1], bodyLines);

            var rowFragments = bodyFragments
                .Where(item => item.Top >= rowStart && item.Top < rowEnd)
                .ToList();

            foreach (var columnAnchor in columnCenters)
            {
                var cellFragments = rowFragments
                    .Where(item => CriticalTableParserSupport.ResolveColumn(item.CenterX, columnCenters) == columnAnchor.Key)
                    .OrderBy(item => item.Top)
                    .ThenBy(item => item.Left)
                    .ToList();

                if (cellFragments.Count == 0)
                {
                    validationErrors.Add($"Missing content for roll band '{rowAnchors[rowIndex].Label}', column '{columnAnchor.Key}'.");
                    continue;
                }

                cellEntries.Add(new ColumnarCellEntry(
                    null,
                    rowAnchors[rowIndex].Label,
                    rowIndex,
                    columnAnchor.Key,
                    CriticalTableParserSupport.BuildLines(cellFragments).ToList()));
            }
        }

        CriticalTableParserSupport.RepairLeadingAffixLeakage(cellEntries, affixLegendSymbols);

        var parsedCells = new List<ParsedCriticalCellArtifact>();
        var parsedResults = new List<ParsedCriticalResult>();
        CriticalTableParserSupport.BuildParsedArtifacts(cellEntries, affixLegend, parsedCells, parsedResults, validationErrors);

        if (columnCenters.Count != 5)
        {
            validationErrors.Add($"Expected 5 standard-table columns but found {columnCenters.Count}.");
        }

        if (parsedCells.Count != rowAnchors.Count * columnCenters.Count)
        {
            validationErrors.Add(
                $"Expected {rowAnchors.Count * columnCenters.Count} parsed cells but produced {parsedCells.Count}.");
        }

        var validationReport = new ImportValidationReport(
            validationErrors.Count == 0,
            validationErrors,
            validationWarnings,
            rowAnchors.Count,
            parsedCells.Count);

        var table = new ParsedCriticalTable(
            entry.Slug,
            entry.DisplayName,
            entry.Family,
            Path.GetFileName(entry.PdfPath),
            "Imported from PDF XML extraction.",
            [],
            columnCenters.Select((item, index) => new ParsedCriticalColumn(item.Key, item.Key, "severity", index + 1)).ToList(),
            parsedRollBands,
            parsedResults);

        return new CriticalTableParseResult(table, pageGeometries, fragments, parsedCells, validationReport);
    }

    private static List<XmlTextFragment> FindHeaderFragments(IReadOnlyList<XmlTextFragment> fragments)
    {
        var headerCandidates = fragments
            .Where(item => item.Text.Length == 1 && char.IsLetter(item.Text[0]))
            .OrderBy(item => item.Top)
            .ThenBy(item => item.Left)
            .ToList();

        foreach (var group in CriticalTableParserSupport.GroupByTop(headerCandidates))
        {
            var ordered = group.OrderBy(item => item.Left).ToList();
            var labels = ordered.Select(item => item.Text.ToUpperInvariant()).ToList();
            if (labels.SequenceEqual(["A", "B", "C", "D", "E"]))
            {
                return ordered;
            }
        }

        throw new InvalidOperationException("Could not find the standard-table A-E header row in the XML artifact.");
    }
}
