namespace RolemasterDb.CriticalParsing;

public static class CriticalCellTextParser
{
    public static CriticalCellParseContent Parse(string rawCellText, AffixLegend affixLegend)
    {
        var lines = rawCellText
            .Split(["\r\n", "\n", "\r"], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
            .ToList();
        return Parse(lines, affixLegend);
    }

    public static CriticalCellParseContent Parse(IReadOnlyList<string> lines, AffixLegend affixLegend)
    {
        var validationErrors = new List<string>();
        var branchStartIndexes = FindBranchStartIndexes(lines);
        var baseLineCount = branchStartIndexes.Count == 0 ? lines.Count : branchStartIndexes[0];
        var baseLines = lines.Take(baseLineCount).ToList();
        var branches = new List<ParsedCriticalBranch>();
        var affixLegendSymbols = affixLegend.ClassificationSymbols;

        validationErrors.AddRange(ValidateSegmentCount(baseLines, affixLegendSymbols, "Base content"));

        for (var branchIndex = 0; branchIndex < branchStartIndexes.Count; branchIndex++)
        {
            var startIndex = branchStartIndexes[branchIndex];
            var endIndex = branchIndex == branchStartIndexes.Count - 1
                ? lines.Count
                : branchStartIndexes[branchIndex + 1];

            branches.Add(ParseBranch(
                lines.Skip(startIndex).Take(endIndex - startIndex).ToList(),
                branchIndex + 1,
                affixLegend,
                validationErrors));
        }

        var (rawText, descriptionText, rawAffixText) = BuildTextSections(baseLines, affixLegendSymbols);
        var effects = AffixEffectParser.Parse(rawAffixText, affixLegend);
        return new CriticalCellParseContent(baseLines, rawText, descriptionText, rawAffixText, effects, branches, validationErrors, []);
    }

    private static ParsedCriticalBranch ParseBranch(
        IReadOnlyList<string> branchLines,
        int sortOrder,
        AffixLegend affixLegend,
        List<string> validationErrors)
    {
        var firstLine = branchLines[0];
        var separatorIndex = firstLine.IndexOf(':', StringComparison.Ordinal);
        var conditionText = CriticalCellParserSupport.CollapseWhitespace(firstLine[..separatorIndex]);
        var firstPayloadLine = CriticalCellParserSupport.CollapseWhitespace(firstLine[(separatorIndex + 1)..]);
        var payloadLines = new List<string>();

        if (!string.IsNullOrWhiteSpace(firstPayloadLine))
        {
            payloadLines.Add(firstPayloadLine);
        }

        foreach (var continuationLine in branchLines.Skip(1))
        {
            var normalized = CriticalCellParserSupport.CollapseWhitespace(continuationLine);
            if (!string.IsNullOrWhiteSpace(normalized))
            {
                payloadLines.Add(normalized);
            }
        }

        var affixLegendSymbols = affixLegend.ClassificationSymbols;
        validationErrors.AddRange(ValidateSegmentCount(payloadLines, affixLegendSymbols, $"Branch '{conditionText}'"));

        var (_, descriptionText, rawAffixText) = BuildTextSections(payloadLines, affixLegendSymbols);
        var effects = AffixEffectParser.Parse(rawAffixText, affixLegend);
        return new ParsedCriticalBranch(
            "conditional",
            CriticalCellParserSupport.NormalizeConditionKey(conditionText),
            conditionText,
            string.Join(Environment.NewLine, branchLines),
            descriptionText,
            rawAffixText,
            effects,
            sortOrder);
    }

    private static List<int> FindBranchStartIndexes(IReadOnlyList<string> lines)
    {
        var branchStartIndexes = new List<int>();

        for (var index = 0; index < lines.Count; index++)
        {
            if (CriticalCellParserSupport.IsConditionalBranchStartLine(lines[index]))
            {
                branchStartIndexes.Add(index);
            }
        }

        return branchStartIndexes;
    }

    private static IReadOnlyList<string> ValidateSegmentCount(
        IReadOnlyList<string> lines,
        IReadOnlySet<string> affixLegendSymbols,
        string scope)
    {
        if (lines.Count == 0)
        {
            return [];
        }

        var segmentCount = CriticalCellParserSupport.CountLineTypeSegments(lines, affixLegendSymbols);
        return segmentCount > 2
            ? [$"{scope} interleaves prose and affix lines."]
            : [];
    }

    private static (string RawText, string DescriptionText, string? RawAffixText) BuildTextSections(
        IReadOnlyList<string> lines,
        IReadOnlySet<string> affixLegendSymbols)
    {
        var rawText = string.Join(Environment.NewLine, lines);
        var rawAffixLines = lines.Where(line => CriticalCellParserSupport.IsAffixLikeLine(line, affixLegendSymbols)).ToList();
        var descriptionLines = lines.Where(line => !CriticalCellParserSupport.IsAffixLikeLine(line, affixLegendSymbols)).ToList();
        var descriptionText = CriticalCellParserSupport.CollapseWhitespace(string.Join(' ', descriptionLines));
        var rawAffixText = rawAffixLines.Count == 0 ? null : string.Join(Environment.NewLine, rawAffixLines);
        return (rawText, descriptionText, rawAffixText);
    }
}
