"""
TypeScript Interface Extractor.

Parses TypeScript interface definitions from .tsx/.ts files
to enable validation against Pydantic schemas.

Uses regex-based extraction to avoid Node.js dependencies,
making it suitable for Python-only CI environments.
"""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from collections.abc import Iterator


@dataclass
class TSProperty:
    """A property within a TypeScript interface."""

    name: str
    ts_type: str
    optional: bool
    description: str | None = None

    def __repr__(self) -> str:
        opt = "?" if self.optional else ""
        return f"{self.name}{opt}: {self.ts_type}"


@dataclass
class TSInterface:
    """A TypeScript interface definition."""

    name: str
    source_file: str
    properties: dict[str, TSProperty] = field(default_factory=dict)
    extends: list[str] = field(default_factory=list)

    def __repr__(self) -> str:
        ext = f" extends {', '.join(self.extends)}" if self.extends else ""
        return f"interface {self.name}{ext} {{ {len(self.properties)} props }}"


class TypeScriptInterfaceExtractor:
    """
    Extract interface definitions from TypeScript files.

    Parses export interface declarations and their properties,
    including JSDoc comments for descriptions.

    Usage:
        extractor = TypeScriptInterfaceExtractor(Path("lightwave-ui/src"))
        interfaces = extractor.extract_all()

        for name, interface in interfaces.items():
            print(f"{name}: {len(interface.properties)} properties")
    """

    # Match: export interface FooProps extends Bar, Baz { ... }
    INTERFACE_PATTERN = re.compile(
        r"export\s+interface\s+(\w+Props?)"  # interface name ending in Props
        r"(?:\s+extends\s+([\w\s,<>]+?))?"  # optional extends clause
        r"\s*\{",  # opening brace
        re.MULTILINE,
    )

    # Match: /** description */ propertyName?: TypeName;
    PROPERTY_PATTERN = re.compile(
        r"(?:"  # Optional JSDoc comment
        r"/\*\*\s*"  # /** start
        r"((?:[^*]|\*(?!/))*?)"  # capture comment content (non-greedy)
        r"\s*\*/"  # */ end
        r"\s*)?"  # end optional group
        r"(\w+)"  # property name
        r"(\?)?"  # optional marker
        r"\s*:\s*"  # colon separator
        r"([^;]+)"  # type (everything until semicolon)
        r";",  # semicolon
        re.MULTILINE | re.DOTALL,
    )

    def __init__(self, ui_src_path: Path | None = None):
        """
        Initialize extractor with path to lightwave-ui/src.

        Args:
            ui_src_path: Path to lightwave-ui/src directory.
                        If None, attempts to locate automatically.
        """
        self.ui_src_path = ui_src_path or self._find_ui_src()
        self._cache: dict[str, TSInterface] = {}

    def _find_ui_src(self) -> Path:
        """Locate lightwave-ui/src relative to lightwave-core."""
        # Navigate from lightwave-core to workspace root
        current = Path(__file__).resolve()
        # Go up: ui_alignment -> schema -> lightwave -> lightwave-core -> packages -> workspace
        workspace = current.parent.parent.parent.parent.parent

        ui_src = workspace / "packages" / "lightwave-ui" / "src"
        if ui_src.exists():
            return ui_src

        # Try alternative location (monorepo root)
        alt_path = workspace / "lightwave-ui" / "src"
        if alt_path.exists():
            return alt_path

        msg = f"Could not locate lightwave-ui/src from {workspace}"
        raise FileNotFoundError(msg)

    def extract_from_file(self, file_path: Path) -> list[TSInterface]:
        """
        Extract all Props interfaces from a single file.

        Args:
            file_path: Path to .tsx or .ts file

        Returns:
            List of extracted interfaces
        """
        try:
            content = file_path.read_text(encoding="utf-8")
        except (OSError, UnicodeDecodeError):
            return []

        interfaces = []

        for match in self.INTERFACE_PATTERN.finditer(content):
            name = match.group(1)
            extends_str = match.group(2) or ""

            # Parse extends list (handling generics like Omit<Foo, 'bar'>)
            extends = self._parse_extends(extends_str) if extends_str else []

            # Find interface body (match braces)
            body_start = match.end()
            body = self._extract_body(content, body_start)

            if body is None:
                continue

            interface = TSInterface(
                name=name,
                source_file=str(file_path.relative_to(self.ui_src_path)),
                extends=extends,
            )

            # Extract properties from body
            for prop_match in self.PROPERTY_PATTERN.finditer(body):
                doc = prop_match.group(1)
                prop_name = prop_match.group(2)
                optional = prop_match.group(3) == "?"
                prop_type = prop_match.group(4).strip()

                # Clean up JSDoc comment
                description = None
                if doc:
                    # Remove * prefixes from multi-line comments
                    description = re.sub(r"^\s*\*\s*", "", doc, flags=re.MULTILINE)
                    description = " ".join(description.split()).strip()
                    if not description:
                        description = None

                interface.properties[prop_name] = TSProperty(
                    name=prop_name,
                    ts_type=prop_type,
                    optional=optional,
                    description=description,
                )

            interfaces.append(interface)
            self._cache[name] = interface

        return interfaces

    def _parse_extends(self, extends_str: str) -> list[str]:
        """Parse extends clause handling generics."""
        extends = []
        depth = 0
        current = ""

        for char in extends_str:
            if char == "<":
                depth += 1
                current += char
            elif char == ">":
                depth -= 1
                current += char
            elif char == "," and depth == 0:
                if current.strip():
                    extends.append(current.strip())
                current = ""
            else:
                current += char

        if current.strip():
            extends.append(current.strip())

        return extends

    def _extract_body(self, content: str, start: int) -> str | None:
        """Extract interface body by matching braces."""
        depth = 1
        pos = start

        while pos < len(content) and depth > 0:
            char = content[pos]
            if char == "{":
                depth += 1
            elif char == "}":
                depth -= 1
            pos += 1

        if depth != 0:
            return None

        return content[start : pos - 1]

    def extract_all(self) -> dict[str, TSInterface]:
        """
        Extract interfaces from all component files.

        Scans all .tsx and .ts files in lightwave-ui/src,
        extracting interfaces that end with 'Props'.

        Returns:
            Dictionary mapping interface names to TSInterface objects
        """
        self._cache.clear()

        for file_path in self._iter_ts_files():
            self.extract_from_file(file_path)

        return self._cache

    def _iter_ts_files(self) -> Iterator[Path]:
        """Iterate over TypeScript files, excluding tests and node_modules."""
        for pattern in ("**/*.tsx", "**/*.ts"):
            for file_path in self.ui_src_path.glob(pattern):
                # Skip test files and node_modules
                if ".test." in file_path.name:
                    continue
                if "node_modules" in str(file_path):
                    continue
                if "__tests__" in str(file_path):
                    continue
                yield file_path

    def get_interface(self, name: str) -> TSInterface | None:
        """Get a specific interface by name."""
        return self._cache.get(name)

    def get_interfaces_by_file(self, file_pattern: str) -> dict[str, TSInterface]:
        """Get interfaces from files matching a pattern."""
        return {name: iface for name, iface in self._cache.items() if file_pattern in iface.source_file}
