using NubLang.Ast;
using NubLang.Syntax;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

namespace NubLang.LSP;

public static class AstExtensions
{
    public static Location ToLocation(this Node node)
    {
        if (node.Tokens.Count == 0)
        {
            return new Location();
        }

        return new Location
        {
            Uri = node.Tokens.First().Span.FilePath,
            Range = new Range(node.Tokens.First().Span.Start.Line - 1, node.Tokens.First().Span.Start.Column - 1, node.Tokens.Last().Span.End.Line - 1, node.Tokens.Last().Span.End.Column - 1)
        };
    }

    public static bool ContainsPosition(this Node node, int line, int character)
    {
        if (node.Tokens.Count == 0)
        {
            return false;
        }

        var start = node.Tokens.First().Span.Start;
        var end = node.Tokens.Last().Span.End;

        var startLine = start.Line - 1;
        var startChar = start.Column - 1;
        var endLine = end.Line - 1;
        var endChar = end.Column - 1;

        if (line < startLine || line > endLine) return false;

        if (line > startLine && line < endLine) return true;

        if (startLine == endLine)
        {
            return character >= startChar && character <= endChar;
        }

        if (line == startLine)
        {
            return character >= startChar;
        }

        if (line == endLine)
        {
            return character <= endChar;
        }

        return false;
    }

    public static FuncNode? FunctionAtPosition(this CompilationUnit compilationUnit, int line, int character)
    {
        return compilationUnit
            .Functions
            .FirstOrDefault(x => x.ContainsPosition(line, character));
    }

    public static Node? DeepestNodeAtPosition(this CompilationUnit compilationUnit, int line, int character)
    {
        return compilationUnit.Functions
            .SelectMany(x => x.DescendantsAndSelf())
            .Where(n => n.ContainsPosition(line, character))
            .OrderBy(n => n.Tokens.First().Span.Start.Line)
            .ThenBy(n => n.Tokens.First().Span.Start.Column)
            .LastOrDefault();
    }
}