"""Plugin discovery from pyproject.toml without importing the plugin.

Parses entry points, module paths, and routes prefix by inspecting files
statically — no plugin code is executed.
"""

from __future__ import annotations

import re
import sys
from dataclasses import dataclass
from pathlib import Path

try:
    import tomllib
except ModuleNotFoundError:  # Python < 3.11
    import tomli as tomllib  # type: ignore[no-redef]


@dataclass(slots=True)
class DiscoveredPlugin:
    """Statically discovered plugin metadata."""

    entry_point_name: str  # "drp-analysis"
    module_path: str  # "mld_plugin_drp.plugin"
    class_name: str  # "DRPAnalysisPlugin"
    factory_target: str  # "mld_plugin_drp.plugin:create_standalone_app"
    routes_prefix: str  # "/drp" (from source) or "/drp-analysis" (fallback)
    project_name: str  # "mld-plugin-drp"
    project_version: str | None  # "0.2.0" or None if dynamic
    project_description: str  # from [project].description
    has_frontend: bool  # frontend/package.json exists


def _read_full_pyproject(project_dir: Path) -> dict:
    """Read and return the full pyproject.toml as a dict."""
    path = project_dir / "pyproject.toml"
    if not path.exists():
        print(f"Error: {path} not found.", file=sys.stderr)
        sys.exit(1)
    with open(path, "rb") as f:
        return tomllib.load(f)


def _parse_entry_point(data: dict) -> tuple[str, str, str] | None:
    """Extract (name, module_path, class_name) from [project.entry-points."mld.plugins"].

    Returns None if no mld.plugins entry point is found.
    """
    entry_points = (
        data.get("project", {}).get("entry-points", {}).get("mld.plugins", {})
    )
    if not entry_points:
        return None

    # Take the first entry point
    name, value = next(iter(entry_points.items()))
    # value is "mld_plugin_drp.plugin:DRPAnalysisPlugin"
    if ":" not in value:
        return None
    module_path, class_name = value.rsplit(":", 1)
    return name, module_path, class_name


def _parse_routes_prefix_from_source(
    module_path: str, project_dir: Path
) -> str | None:
    """Parse PLUGIN_ROUTES_PREFIX from the plugin source file without importing it.

    Looks for patterns like:
        PLUGIN_ROUTES_PREFIX = "/drp"
        PLUGIN_ROUTES_PREFIX = '/drp'
    """
    # Convert module path to file path: mld_plugin_drp.plugin -> src/mld_plugin_drp/plugin.py
    parts = module_path.split(".")
    candidates = [
        project_dir / "src" / Path(*parts).with_suffix(".py"),
        project_dir / Path(*parts).with_suffix(".py"),
    ]

    for source_path in candidates:
        if not source_path.exists():
            continue
        try:
            content = source_path.read_text()
        except OSError:
            continue

        match = re.search(
            r'PLUGIN_ROUTES_PREFIX\s*=\s*["\'](/[^"\']+)["\']', content
        )
        if match:
            return match.group(1)

    return None


def discover_plugin(project_dir: Path) -> DiscoveredPlugin:
    """Discover plugin metadata from pyproject.toml and source files.

    Raises SystemExit if the project directory lacks required configuration.
    """
    data = _read_full_pyproject(project_dir)

    project = data.get("project")
    if not project:
        print("Error: pyproject.toml has no [project] table.", file=sys.stderr)
        sys.exit(1)

    entry_point = _parse_entry_point(data)
    if not entry_point:
        print(
            "Error: No [project.entry-points.\"mld.plugins\"] found in pyproject.toml.",
            file=sys.stderr,
        )
        sys.exit(1)

    ep_name, module_path, class_name = entry_point

    # Routes prefix: parse from source, fallback to /{entry_point_name}
    routes_prefix = _parse_routes_prefix_from_source(module_path, project_dir)
    if routes_prefix is None:
        routes_prefix = f"/{ep_name}"

    # Factory target for uvicorn --factory
    factory_target = f"{module_path}:create_standalone_app"

    # Version: static or None if dynamic
    version = project.get("version")

    has_frontend = (project_dir / "frontend" / "package.json").exists()

    return DiscoveredPlugin(
        entry_point_name=ep_name,
        module_path=module_path,
        class_name=class_name,
        factory_target=factory_target,
        routes_prefix=routes_prefix,
        project_name=project["name"],
        project_version=version,
        project_description=project.get("description", ""),
        has_frontend=has_frontend,
    )


def get_sdk_dependency_spec(project_dir: Path) -> str | None:
    """Extract the mld-sdk version specifier from pyproject.toml dependencies.

    Returns e.g. ">=0.9.4" or None if mld-sdk is not in dependencies.
    """
    data = _read_full_pyproject(project_dir)
    deps = data.get("project", {}).get("dependencies", [])
    for dep in deps:
        # Match "mld-sdk>=0.9.4", "mld-sdk>=0.9.4,<1.0", "mld-sdk"
        match = re.match(r"mld[-_]sdk\s*(.*)", dep, re.IGNORECASE)
        if match:
            spec = match.group(1).strip()
            return spec if spec else "*"
    return None
