"""JavaScript/TypeScript syntax validation.

Uses Node.js for validation when available, with a basic regex fallback.
"""

from __future__ import annotations

import logging
from functools import lru_cache
from typing import TYPE_CHECKING

import tree_sitter_javascript
import tree_sitter_typescript
from tree_sitter import Language, Parser

from aiservice.common.markdown_utils import split_markdown_code

if TYPE_CHECKING:
    from tree_sitter import Node

js_parser = Parser(Language(tree_sitter_javascript.language()))

# Use tsx parser for TypeScript to support JSX syntax (TSX is a superset of TS)
ts_parser = Parser(Language(tree_sitter_typescript.language_tsx()))


@lru_cache(maxsize=200)
def _validate(code: str, lang: str) -> bool:
    parser = js_parser if lang == "js" else ts_parser
    tree = parser.parse(code.encode("utf8"))
    return not tree.root_node.has_error


def _find_error_location(code: str, lang: str) -> str | None:
    """Find the location of the first syntax error in the code."""
    parser = js_parser if lang == "js" else ts_parser
    tree = parser.parse(code.encode("utf8"))
    if not tree.root_node.has_error:
        return None

    def find_error(node: Node) -> tuple[int, int] | None:
        if node.type == "ERROR":
            return node.start_point
        for child in node.children:
            result = find_error(child)
            if result:
                return result
        return None

    error_point = find_error(tree.root_node)
    if error_point:
        line, col = error_point
        lines = code.split("\n")
        if line < len(lines):
            error_line = lines[line]
            return f"line {line + 1}, col {col}: {error_line[:80]}"
        return f"line {line + 1}, col {col}"
    return "unknown location"


@lru_cache(maxsize=100)
def validate_javascript_syntax(code: str) -> tuple[bool, str | None]:
    if code.strip().startswith("```"):
        # markdown code block
        file_to_code = split_markdown_code(code, "javascript")
        if not file_to_code:
            msg = f"No JavaScript code blocks found in markdown. Code starts with: {code[:100]!r}"
            logging.warning(msg)
            # Fall through to validate the raw code
        else:
            for filepath, _code in file_to_code.items():
                valid = _validate(_code, "js")
                if not valid:
                    error_loc = _find_error_location(_code, "js")
                    msg = f"Invalid JavaScript syntax in {filepath}: {error_loc}. Code snippet: {_code[:200]!r}"
                    logging.error(msg)
                    return False, f"Invalid syntax at {error_loc}"
            return True, None

    valid = _validate(code, "js")
    if not valid:
        error_loc = _find_error_location(code, "js")
        msg = f"Invalid JavaScript syntax: {error_loc}. Code snippet: {code[:200]!r}"
        logging.error(msg)
        return False, f"Invalid syntax at {error_loc}"
    return True, None


@lru_cache(maxsize=100)
def validate_typescript_syntax(code: str) -> tuple[bool, str | None]:
    if code.strip().startswith("```"):
        # markdown code block
        file_to_code = split_markdown_code(code, "typescript")
        if not file_to_code:
            msg = f"No TypeScript code blocks found in markdown. Code starts with: {code[:100]!r}"
            logging.warning(msg)
            # Fall through to validate the raw code
        else:
            for filepath, _code in file_to_code.items():
                valid = _validate(_code, "ts")
                if not valid:
                    error_loc = _find_error_location(_code, "ts")
                    msg = f"Invalid TypeScript syntax in {filepath}: {error_loc}. Code snippet: {_code[:200]!r}"
                    logging.error(msg)
                    return False, f"Invalid syntax at {error_loc}"
            return True, None

    valid = _validate(code, "ts")
    if not valid:
        error_loc = _find_error_location(code, "ts")
        msg = f"Invalid TypeScript syntax: {error_loc}. Code snippet: {code[:200]!r}"
        logging.error(msg)
        return False, f"Invalid syntax at {error_loc}"
    return True, None
