namespace RolemasterDb.CriticalParsing;

public static class CriticalQuickNotationParser
{
    public static CriticalCellParseContent Parse(string quickParseInput, AffixLegend affixLegend)
    {
        var lines = quickParseInput
            .Split(["\r\n", "\n", "\r"], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
            .Select(CriticalCellParserSupport.CollapseWhitespace)
            .Where(line => !string.IsNullOrWhiteSpace(line))
            .ToList();

        if (lines.Count == 0)
        {
            return new CriticalCellParseContent([], string.Empty, string.Empty, null, [], [], [], []);
        }

        var validationErrors = new List<string>();
        var tokenReviewIssues = new List<CriticalTokenReviewIssue>();
        var descriptionText = lines[0];
        var rawAffixLines = new List<string>();
        var effects = new List<ParsedCriticalEffect>();
        var branches = new List<ParsedCriticalBranch>();

        foreach (var line in lines.Skip(1))
        {
            if (line.Contains(':', StringComparison.Ordinal))
            {
                branches.Add(ParseBranch(line, branches.Count + 1, affixLegend, validationErrors, tokenReviewIssues));
                continue;
            }

            var normalizedAffixLine = ParseBaseAffixLine(line, affixLegend, validationErrors, tokenReviewIssues, effects);
            if (!string.IsNullOrWhiteSpace(normalizedAffixLine))
            {
                rawAffixLines.Add(normalizedAffixLine);
            }
        }

        return new CriticalCellParseContent(
            lines,
            string.Join(Environment.NewLine, lines),
            descriptionText,
            rawAffixLines.Count == 0 ? null : string.Join(Environment.NewLine, rawAffixLines),
            effects,
            branches,
            validationErrors,
            tokenReviewIssues);
    }

    private static string? ParseBaseAffixLine(
        string line,
        AffixLegend affixLegend,
        List<string> validationErrors,
        List<CriticalTokenReviewIssue> tokenReviewIssues,
        List<ParsedCriticalEffect> effects)
    {
        var tokens = SplitPayload(line);
        var normalizedTokens = new List<string>();

        foreach (var token in tokens)
        {
            var parseResult = AffixEffectParser.ParseAffixToken(token, affixLegend);
            if (parseResult.Effects.Count > 0)
            {
                normalizedTokens.Add(parseResult.NormalizedToken);
                effects.AddRange(parseResult.Effects);
            }

            AddTokenReviewIssues(
                tokenReviewIssues,
                validationErrors,
                scope: "base",
                conditionText: null,
                originalToken: token,
                parseResult);
        }

        return normalizedTokens.Count == 0 ? null : string.Join(", ", normalizedTokens);
    }

    private static ParsedCriticalBranch ParseBranch(
        string line,
        int sortOrder,
        AffixLegend affixLegend,
        List<string> validationErrors,
        List<CriticalTokenReviewIssue> tokenReviewIssues)
    {
        var separatorIndex = line.IndexOf(':', StringComparison.Ordinal);
        var conditionText = CriticalCellParserSupport.CollapseWhitespace(line[..separatorIndex]);
        var payload = line[(separatorIndex + 1)..];
        var tokens = SplitPayload(payload);
        var descriptionTokens = new List<string>();
        var normalizedAffixTokens = new List<string>();
        var effects = new List<ParsedCriticalEffect>();
        var seenAffix = false;

        foreach (var token in tokens)
        {
            var parseResult = AffixEffectParser.ParseAffixToken(token, affixLegend);
            if (parseResult.Effects.Count > 0)
            {
                seenAffix = true;
                normalizedAffixTokens.Add(parseResult.NormalizedToken);
                effects.AddRange(parseResult.Effects);
                AddTokenReviewIssues(
                    tokenReviewIssues,
                    validationErrors,
                    scope: "branch",
                    conditionText,
                    originalToken: token,
                    parseResult);
                continue;
            }

            if (seenAffix || LooksLikeAffixToken(token, affixLegend))
            {
                AddTokenReviewIssues(
                    tokenReviewIssues,
                    validationErrors,
                    scope: "branch",
                    conditionText,
                    originalToken: token,
                    parseResult);
                continue;
            }

            descriptionTokens.Add(token);
        }

        var descriptionText = descriptionTokens.Count == 0
            ? string.Empty
            : CriticalCellParserSupport.CollapseWhitespace(string.Join(", ", descriptionTokens));
        var rawAffixText = normalizedAffixTokens.Count == 0
            ? null
            : string.Join(", ", normalizedAffixTokens);

        return new ParsedCriticalBranch(
            "conditional",
            CriticalCellParserSupport.NormalizeConditionKey(conditionText),
            conditionText,
            line,
            descriptionText,
            rawAffixText,
            effects,
            sortOrder);
    }

    private static IReadOnlyList<string> SplitPayload(string payload) =>
        payload
            .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
            .Select(CriticalCellParserSupport.CollapseWhitespace)
            .Where(token => !string.IsNullOrWhiteSpace(token))
            .ToList();

    private static void AddTokenReviewIssues(
        List<CriticalTokenReviewIssue> tokenReviewIssues,
        List<string> validationErrors,
        string scope,
        string? conditionText,
        string originalToken,
        AffixTokenParseResult parseResult)
    {
        if (parseResult.UnresolvedFragments.Count == 0)
        {
            return;
        }

        var reviewText = BuildReviewText(scope, conditionText, originalToken, parseResult);
        var issue = new CriticalTokenReviewIssue(
            scope,
            conditionText,
            CriticalCellParserSupport.CollapseWhitespace(originalToken),
            reviewText);

        tokenReviewIssues.Add(issue);
        validationErrors.Add(reviewText);
    }

    private static string BuildReviewText(
        string scope,
        string? conditionText,
        string originalToken,
        AffixTokenParseResult parseResult)
    {
        var normalizedToken = CriticalCellParserSupport.CollapseWhitespace(originalToken);
        var scopeLabel = scope == "branch"
            ? $"Branch '{conditionText}'"
            : "Base content";

        if (parseResult.Effects.Count == 0)
        {
            return $"{scopeLabel} has unrecognized affix token '{normalizedToken}'.";
        }

        var unresolvedText = string.Join(", ", parseResult.UnresolvedFragments.Select(fragment => $"'{fragment}'"));
        var fragmentLabel = parseResult.UnresolvedFragments.Count == 1 ? "fragment" : "fragments";
        return $"{scopeLabel} token '{normalizedToken}' still has unresolved {fragmentLabel} {unresolvedText}.";
    }

    private static bool LooksLikeAffixToken(string token, AffixLegend affixLegend)
    {
        var normalized = CriticalCellParserSupport.CollapseWhitespace(token);
        if (string.IsNullOrWhiteSpace(normalized))
        {
            return false;
        }

        if (normalized.Any(char.IsDigit) ||
            normalized.StartsWith('+') ||
            normalized.StartsWith('-'))
        {
            return true;
        }

        if (normalized.Contains("pp", StringComparison.OrdinalIgnoreCase))
        {
            return true;
        }

        return affixLegend.EffectSymbols.Any(symbol => normalized.Contains(symbol, StringComparison.Ordinal));
    }
}
