import pytest
import os, sys

# make sure the `src` directory is on sys.path so we can import the
# neuromath package. running the module directly from tests/ means the
# current working directory is the project root, so the relative path
# works reliably.
root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, os.path.join(root, 'src'))

from neuromath.lexer import Lexer
from neuromath.lexer import Token, TokenType

def tokenize(code):
    lexer = Lexer(code)
    return lexer.tokenize()

def test_basic_tokens():
    code = "3 + 4 * (2 - 1)"
    tokens = tokenize(code)
    expected = [
        Token(TokenType.NUMBER, '3'),
        Token(TokenType.PLUS, '+'),
        Token(TokenType.NUMBER, '4'),
        Token(TokenType.MULTIPLY, '*'),
        Token(TokenType.LPAREN, '('),
        Token(TokenType.NUMBER, '2'),
        Token(TokenType.MINUS, '-'),
        Token(TokenType.NUMBER, '1'),
        Token(TokenType.RPAREN, ')')
    ]
    assert tokens == expected

def test_variables_and_functions():
    code = "sin(x) + cos(y)"
    tokens = tokenize(code)
    expected = [
        Token(TokenType.FUNCTION, 'sin'),
        Token(TokenType.LPAREN, '('),
        Token(TokenType.VARIABLE, 'x'),
        Token(TokenType.RPAREN, ')'),
        Token(TokenType.PLUS, '+'),
        Token(TokenType.FUNCTION, 'cos'),
        Token(TokenType.LPAREN, '('),
        Token(TokenType.VARIABLE, 'y'),
        Token(TokenType.RPAREN, ')')
    ]
    assert tokens == expected

def test_matrix_and_vectors():
    code = "[1, 2; 3, 4] * [5; 6]"
    tokens = tokenize(code)
    expected = [
        Token(TokenType.LBRACKET, '['),
        Token(TokenType.NUMBER, '1'),
        Token(TokenType.COMMA, ','),
        Token(TokenType.NUMBER, '2'),
        Token(TokenType.SEMICOLON, ';'),
        Token(TokenType.NUMBER, '3'),
        Token(TokenType.COMMA, ','),
        Token(TokenType.NUMBER, '4'),
        Token(TokenType.RBRACKET, ']'),
        Token(TokenType.MULTIPLY, '*'),
        Token(TokenType.LBRACKET, '['),
        Token(TokenType.NUMBER, '5'),
        Token(TokenType.SEMICOLON, ';'),
        Token(TokenType.NUMBER, '6'),
        Token(TokenType.RBRACKET, ']')
    ]
    assert tokens == expected

def test_unary_operators():
    code = "-3 + +4 + --5"
    tokens = tokenize(code)
    expected = [
        Token(TokenType.MINUS, '-'),
        Token(TokenType.NUMBER, '3'),
        Token(TokenType.PLUS, '+'),
        Token(TokenType.PLUS, '+'),
        Token(TokenType.NUMBER, '4'),
        Token(TokenType.PLUS, '+'),
        Token(TokenType.MINUS, '-'),
        Token(TokenType.MINUS, '-'),
        Token(TokenType.NUMBER, '5')
    ]
    assert tokens == expected

def test_whitespace():
    code = "   3    +   4   \n # This is a comment\n  *  ( 2 - 1 )   "
    tokens = tokenize(code)
    expected = [
        Token(TokenType.NUMBER, '3'),
        Token(TokenType.PLUS, '+'),
        Token(TokenType.NUMBER, '4'),
        Token(TokenType.NEWLINE, '\n'),
        Token(TokenType.IDENTIFIER, '# This is a comment'),
        Token(TokenType.MULTIPLY, '*'),
        Token(TokenType.LPAREN, '('),
        Token(TokenType.NUMBER, '2'),
        Token(TokenType.MINUS, '-'),
        Token(TokenType.NUMBER, '1'),
        Token(TokenType.RPAREN, ')')
    ]
    assert tokens == expected

def test_invalid_characters():
    code = "3 + @"
    with pytest.raises(ValueError):
        tokenize(code)
