namespace NATS.Server.TestUtilities.Parity;

public sealed record DriftRow(string Feature, string DifferencesStatus, string EvidenceStatus, string Reason);

public sealed class JetStreamParityTruthMatrixReport
{
    public JetStreamParityTruthMatrixReport(IReadOnlyList<DriftRow> driftRows, IReadOnlyList<string> contradictions)
    {
        DriftRows = driftRows;
        Contradictions = contradictions;
    }

    public IReadOnlyList<DriftRow> DriftRows { get; }
    public IReadOnlyList<string> Contradictions { get; }
}

public static class JetStreamParityTruthMatrix
{
    public static JetStreamParityTruthMatrixReport Load(string differencesRelativePath, string mapRelativePath)
    {
        var repositoryRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ".."));
        var differencesPath = Path.Combine(repositoryRoot, differencesRelativePath);
        var mapPath = Path.Combine(repositoryRoot, mapRelativePath);
        File.Exists(differencesPath).ShouldBeTrue();
        File.Exists(mapPath).ShouldBeTrue();

        var differences = ParityRowInspector.Load(differencesRelativePath).Rows;
        var matrixRows = ParseTruthMatrix(mapPath);
        var drift = new List<DriftRow>();

        if (matrixRows.Count == 0)
        {
            drift.Add(new DriftRow(
                "JetStream Truth Matrix",
                "missing",
                "missing",
                "docs/plans/2026-02-23-jetstream-remaining-parity-map.md must include a populated 'JetStream Truth Matrix' table."));
        }

        foreach (var row in matrixRows)
        {
            var differencesRow = differences.FirstOrDefault(r =>
                string.Equals(r.Feature, row.DifferencesFeature, StringComparison.OrdinalIgnoreCase));

            if (differencesRow is null)
            {
                drift.Add(new DriftRow(
                    row.Feature,
                    "missing",
                    row.EvidenceStatus,
                    $"Differences row '{row.DifferencesFeature}' was not found in differences.md."));
                continue;
            }

            if (!string.Equals(differencesRow.DotNetStatus, "Y", StringComparison.OrdinalIgnoreCase))
            {
                drift.Add(new DriftRow(
                    row.Feature,
                    differencesRow.DotNetStatus,
                    row.EvidenceStatus,
                    "Differences status must be Y for a verified truth-matrix row."));
            }

            if (!string.Equals(row.EvidenceStatus, "verified", StringComparison.OrdinalIgnoreCase))
            {
                drift.Add(new DriftRow(
                    row.Feature,
                    differencesRow.DotNetStatus,
                    row.EvidenceStatus,
                    "Evidence status must be 'verified'."));
            }

            if (string.IsNullOrWhiteSpace(row.TestEvidence) || row.TestEvidence == "-")
            {
                drift.Add(new DriftRow(
                    row.Feature,
                    differencesRow.DotNetStatus,
                    row.EvidenceStatus,
                    "Test evidence must be provided for every truth-matrix row."));
            }
        }

        var contradictions = ParseRemainingExplicitDeltaContradictions(differencesPath, matrixRows);
        return new JetStreamParityTruthMatrixReport(drift, contradictions);
    }

    private static List<TruthMatrixRow> ParseTruthMatrix(string mapPath)
    {
        var rows = new List<TruthMatrixRow>();
        var inTruthMatrix = false;
        foreach (var rawLine in File.ReadLines(mapPath))
        {
            var line = rawLine.Trim();
            if (line.StartsWith("## ", StringComparison.Ordinal))
            {
                inTruthMatrix = string.Equals(
                    line,
                    "## JetStream Truth Matrix",
                    StringComparison.OrdinalIgnoreCase);
                continue;
            }

            if (!inTruthMatrix || !line.StartsWith("|", StringComparison.Ordinal) || line.Contains("---", StringComparison.Ordinal))
                continue;

            var cells = line.Trim('|').Split('|').Select(c => c.Trim()).ToArray();
            if (cells.Length < 4 || string.Equals(cells[0], "Feature", StringComparison.OrdinalIgnoreCase))
                continue;

            rows.Add(new TruthMatrixRow(
                cells[0],
                cells[1],
                cells[2],
                cells[3]));
        }

        return rows;
    }

    private static List<string> ParseRemainingExplicitDeltaContradictions(
        string differencesPath,
        IReadOnlyList<TruthMatrixRow> matrixRows)
    {
        var contradictions = new List<string>();
        var inExplicitDeltas = false;
        var negativeMarkers = new[]
        {
            "unimplemented",
            "still `n`",
            "still n",
            "remains",
            "incomplete",
        };

        foreach (var rawLine in File.ReadLines(differencesPath))
        {
            var line = rawLine.Trim();
            if (line.StartsWith("### ", StringComparison.Ordinal))
            {
                inExplicitDeltas = string.Equals(
                    line,
                    "### Remaining Explicit Deltas",
                    StringComparison.OrdinalIgnoreCase);
                continue;
            }

            if (inExplicitDeltas && line.StartsWith("## ", StringComparison.Ordinal))
            {
                inExplicitDeltas = false;
                continue;
            }

            if (!inExplicitDeltas || !line.StartsWith("- ", StringComparison.Ordinal))
                continue;

            var normalizedLine = line.ToLowerInvariant();
            if (!negativeMarkers.Any(marker => normalizedLine.Contains(marker, StringComparison.Ordinal)))
                continue;

            foreach (var row in matrixRows.Where(r =>
                         string.Equals(r.EvidenceStatus, "verified", StringComparison.OrdinalIgnoreCase)))
            {
                if (MentionsFeature(normalizedLine, row))
                {
                    contradictions.Add($"{row.Feature}: {line[2..].Trim()}");
                    break;
                }
            }
        }

        return contradictions;
    }

    private static bool MentionsFeature(string normalizedLine, TruthMatrixRow row)
    {
        var tokens = Tokenize(row.Feature)
            .Concat(Tokenize(row.DifferencesFeature))
            .Where(t => t.Length >= 4)
            .Distinct(StringComparer.Ordinal)
            .ToArray();

        if (tokens.Length == 0)
            return false;

        var matches = tokens.Count(t => normalizedLine.Contains(t, StringComparison.Ordinal));
        return matches >= 2;
    }

    private static IEnumerable<string> Tokenize(string value)
    {
        var chars = value.ToLowerInvariant()
            .Select(c => char.IsLetterOrDigit(c) ? c : ' ')
            .ToArray();
        return new string(chars)
            .Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
    }

    private sealed record TruthMatrixRow(
        string Feature,
        string DifferencesFeature,
        string EvidenceStatus,
        string TestEvidence);
}
