"""
Basic integration tests for the MATLAB language server functionality.

These tests validate the functionality of the language server APIs
like request_document_symbols using the MATLAB test repository.

Requirements:
    - MATLAB R2021b or later must be installed
    - MATLAB_PATH environment variable should be set to MATLAB installation directory
    - Node.js and npm must be installed
"""

import os

import pytest

from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics

# Skip all tests if MATLAB is not available
pytestmark = pytest.mark.matlab

# Check if MATLAB is available
MATLAB_AVAILABLE = os.environ.get("MATLAB_PATH") is not None or any(
    os.path.exists(p)
    for p in [
        "/Applications/MATLAB_R2024b.app",
        "/Applications/MATLAB_R2025b.app",
        "/Volumes/S1/Applications/MATLAB_R2024b.app",
        "/Volumes/S1/Applications/MATLAB_R2025b.app",
    ]
)


@pytest.mark.skipif(not MATLAB_AVAILABLE, reason="MATLAB installation not found")
class TestMatlabLanguageServerBasics:
    """Test basic functionality of the MATLAB language server."""

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_matlab_language_server_initialization(self, language_server: SolidLanguageServer) -> None:
        """Test that MATLAB language server can be initialized successfully."""
        assert language_server is not None
        assert language_server.language == Language.MATLAB

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_matlab_request_document_symbols_class(self, language_server: SolidLanguageServer) -> None:
        """Test request_document_symbols for MATLAB class file."""
        # Test getting symbols from Calculator.m (class file)
        all_symbols, _root_symbols = language_server.request_document_symbols("Calculator.m").get_all_symbols_and_roots()

        # Extract class symbols (LSP Symbol Kind 5 for class)
        class_symbols = [symbol for symbol in all_symbols if symbol.get("kind") == 5]
        class_names = [symbol["name"] for symbol in class_symbols]

        # Should find the Calculator class
        assert "Calculator" in class_names, "Should find Calculator class"

        # Extract method symbols (LSP Symbol Kind 6 for method or 12 for function)
        method_symbols = [symbol for symbol in all_symbols if symbol.get("kind") in [6, 12]]
        method_names = [symbol["name"] for symbol in method_symbols]

        # Should find key methods
        expected_methods = ["add", "subtract", "multiply", "divide"]
        for method in expected_methods:
            assert method in method_names, f"Should find {method} method in Calculator class"

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_matlab_request_document_symbols_function(self, language_server: SolidLanguageServer) -> None:
        """Test request_document_symbols for MATLAB function file."""
        # Test getting symbols from lib/mathUtils.m (function file)
        all_symbols, _root_symbols = language_server.request_document_symbols("lib/mathUtils.m").get_all_symbols_and_roots()

        # Extract function symbols (LSP Symbol Kind 12 for function)
        function_symbols = [symbol for symbol in all_symbols if symbol.get("kind") == 12]
        function_names = [symbol["name"] for symbol in function_symbols]

        # Should find the main mathUtils function
        assert "mathUtils" in function_names, "Should find mathUtils function"

        # Should also find nested/local functions
        expected_local_functions = ["computeFactorial", "computeFibonacci", "checkPrime", "computeStats"]
        for func in expected_local_functions:
            assert func in function_names, f"Should find {func} local function"

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_matlab_request_document_symbols_script(self, language_server: SolidLanguageServer) -> None:
        """Test request_document_symbols for MATLAB script file."""
        # Test getting symbols from main.m (script file)
        all_symbols, _root_symbols = language_server.request_document_symbols("main.m").get_all_symbols_and_roots()

        # Scripts may have variables and sections, but less structured symbols
        # Just verify we can get symbols without errors
        assert all_symbols is not None


@pytest.mark.skipif(not MATLAB_AVAILABLE, reason="MATLAB installation not found")
class TestMatlabLanguageServerReferences:
    """Test find references functionality of the MATLAB language server."""

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_matlab_find_references_within_file(self, language_server: SolidLanguageServer) -> None:
        """Test finding references within a single MATLAB file."""
        # Find references to 'result' variable in Calculator.m
        # This is a basic test to verify references work
        references = language_server.request_references("Calculator.m", 25, 12)  # 'result' in add method

        # Should find at least the definition
        assert references is not None

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_matlab_find_references_cross_file(self, language_server: SolidLanguageServer) -> None:
        """Test finding references across MATLAB files."""
        # Find references to Calculator class used in main.m
        references = language_server.request_references("main.m", 11, 8)  # 'Calculator' reference

        # Should find references in both main.m and Calculator.m
        assert references is not None

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_bare_symbol_names(self, language_server) -> None:
        all_symbols = request_all_symbols(language_server)
        malformed_symbols = []
        for s in all_symbols:
            if has_malformed_name(s):
                malformed_symbols.append(s)
        if malformed_symbols:
            pytest.fail(
                f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
                pytrace=False,
            )

    @pytest.mark.parametrize("language_server", [Language.MATLAB], indirect=True)
    def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
        assert_file_diagnostics(
            language_server,
            "diagnostics_sample.m",
            (),
            min_count=1,
        )
