using Inspectron.Epson.Templates.Configuration;
using Inspectron.Epson.Templates.Language;
using Inspectron.Epson.Templates.Language.Nodes;

namespace Inspectron.Epson.Templates.Validation;

public class TemplateValidator
{
    private readonly PrinterProfile? _profile;
    private readonly List<TemplateError> _errors = new();
    private readonly List<TemplateWarning> _warnings = new();
    private readonly HashSet<string> _definedVariables = new();

    public TemplateValidator(PrinterProfile? profile = null)
    {
        _profile = profile;
    }

    public TemplateValidationResult Validate(string templateSource)
    {
        _errors.Clear();
        _warnings.Clear();
        _definedVariables.Clear();

        // Lexer phase
        var lexer = new Lexer(templateSource);
        var lexerResult = lexer.Tokenize();

        foreach (var error in lexerResult.Errors)
        {
            _errors.Add(new TemplateError(
                TemplateErrorCode.RTL007_UnclosedStyleBlock,
                error.Message,
                error.Position));
        }

        // Parser phase
        var parser = new Parser(lexerResult.Tokens);
        var parseResult = parser.Parse();

        foreach (var error in parseResult.Errors)
        {
            var code = MapParseError(error.Message);
            _errors.Add(new TemplateError(code, error.Message, error.Position));
        }

        // Semantic validation
        if (_errors.Count == 0)
        {
            ValidateNode(parseResult.Template);
        }

        return new TemplateValidationResult(_errors.ToList(), _warnings.ToList());
    }

    private void ValidateNode(ITemplateNode node)
    {
        switch (node)
        {
            case TemplateNode template:
                foreach (var child in template.ChildNodes)
                {
                    ValidateNode(child);
                }
                break;

            case IfNode ifNode:
                ValidateCondition(ifNode.IfBranch.Condition, ifNode.Position);
                if (ifNode.IfBranch.Body.Count == 0)
                {
                    _warnings.Add(new TemplateWarning(
                        TemplateWarningCode.RTL100_EmptyBlock,
                        "Empty @if block body",
                        ifNode.Position));
                }
                foreach (var child in ifNode.IfBranch.Body)
                {
                    ValidateNode(child);
                }
                foreach (var branch in ifNode.ElseIfBranches)
                {
                    ValidateCondition(branch.Condition, branch.Position);
                    foreach (var child in branch.Body)
                    {
                        ValidateNode(child);
                    }
                }
                if (ifNode.ElseBranch != null)
                {
                    foreach (var child in ifNode.ElseBranch)
                    {
                        ValidateNode(child);
                    }
                }
                break;

            case ForeachNode foreachNode:
                _definedVariables.Add(foreachNode.ItemVariable);
                if (foreachNode.Body.Count == 0)
                {
                    _warnings.Add(new TemplateWarning(
                        TemplateWarningCode.RTL100_EmptyBlock,
                        "Empty @foreach block body",
                        foreachNode.Position));
                }
                foreach (var child in foreachNode.Body)
                {
                    ValidateNode(child);
                }
                _definedVariables.Remove(foreachNode.ItemVariable);
                break;

            case RowNode rowNode:
                ValidateRowWidth(rowNode);
                foreach (var column in rowNode.Columns)
                {
                    ValidateNode(column);
                }
                break;

            case ColumnNode columnNode:
                foreach (var child in columnNode.ContentNodes)
                {
                    ValidateNode(child);
                }
                break;

            case StyledTextNode styledNode:
                ValidateStyles(styledNode.Styles, styledNode.Position);
                foreach (var child in styledNode.ContentNodes)
                {
                    ValidateNode(child);
                }
                break;

            case BindingNode bindingNode:
                ValidateBinding(bindingNode);
                break;

            case TextNode:
            case SeparatorNode:
            case NewLineNode:
                // No additional validation needed
                break;
        }
    }

    private void ValidateCondition(string condition, SourcePosition position)
    {
        if (string.IsNullOrWhiteSpace(condition))
        {
            _errors.Add(new TemplateError(
                TemplateErrorCode.RTL006_InvalidCondition,
                "Empty condition expression",
                position));
        }
    }

    private void ValidateBinding(BindingNode node)
    {
        if (string.IsNullOrWhiteSpace(node.Path))
        {
            _errors.Add(new TemplateError(
                TemplateErrorCode.RTL004_InvalidBinding,
                "Empty binding path",
                node.Position));
        }
    }

    private void ValidateStyles(IReadOnlyList<string> styles, SourcePosition position)
    {
        var validStyles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            "bold", "big", "tall", "red", "center", "right", "left"
        };

        foreach (var style in styles)
        {
            var styleName = style.ToLowerInvariant();
            if (styleName.StartsWith("spacing:"))
            {
                var spacingValue = styleName[8..];
                if (!int.TryParse(spacingValue, out _))
                {
                    _warnings.Add(new TemplateWarning(
                        TemplateWarningCode.RTL102_PossibleNullReference,
                        $"Invalid spacing value: '{spacingValue}'",
                        position));
                }
                continue;
            }

            if (!validStyles.Contains(styleName))
            {
                _warnings.Add(new TemplateWarning(
                    TemplateWarningCode.RTL102_PossibleNullReference,
                    $"Unknown style: '{style}'",
                    position));
            }
        }
    }

    private void ValidateRowWidth(RowNode row)
    {
        if (_profile == null) return;

        var totalWidth = row.Columns.Sum(c => c.Width);
        if (totalWidth > _profile.LineWidth)
        {
            _warnings.Add(new TemplateWarning(
                TemplateWarningCode.RTL103_LineWidthExceeded,
                $"Row total width ({totalWidth}) exceeds printer line width ({_profile.LineWidth})",
                row.Position));
        }
    }

    private TemplateErrorCode MapParseError(string message)
    {
        if (message.Contains("@end"))
            return TemplateErrorCode.RTL002_UnclosedBlock;
        if (message.Contains("foreach"))
            return TemplateErrorCode.RTL005_InvalidForeachSyntax;
        if (message.Contains("unexpected", StringComparison.OrdinalIgnoreCase))
            return TemplateErrorCode.RTL001_UnexpectedToken;

        return TemplateErrorCode.RTL001_UnexpectedToken;
    }
}
