"""API drift detection — deterministic, no AI.

Compares backend route definitions found in the codebase against the Maeris
API registry. Reports:
  - ROUTE_MISSING      : API in Maeris registry has no matching route in code
  - ROUTE_UNREGISTERED : Route found in code is not registered in Maeris
"""

from __future__ import annotations

from pathlib import Path

from .models import DriftIssue, DriftKind, DriftReport, DriftSeverity

# Re-use the route extraction helpers from coverage_cmds without copying
from maeris_mcp.cli.coverage_cmds import (
    _extract_nextjs_routes,
    _extract_routes_from_content,
    _normalize_path,
    _paths_match,
)


async def detect_api_drift(client, scan_root: Path) -> DriftReport:
    """Deterministic API drift detection.

    1. Scans the codebase for HTTP route definitions.
    2. Fetches the Maeris API registry.
    3. Reports routes that exist in Maeris but are gone from code (ROUTE_MISSING)
       and routes in code not registered in Maeris (ROUTE_UNREGISTERED).

    Args:
        client: An active MaerisClient instance.
        scan_root: Root directory to scan for route definitions.

    Returns:
        DriftReport with ROUTE_MISSING and ROUTE_UNREGISTERED issues.
    """
    from maeris_mcp.scanners.code_reader import collect_scan_files, get_file_contents

    # ── Step 1: extract routes from source ───────────────────────────────────
    abs_paths, _ = collect_scan_files(str(scan_root), api_signals_only=True)
    file_contents = get_file_contents(abs_paths)

    repo_routes: list[dict] = []
    seen_global: set[tuple[str, str]] = set()

    # Regex-based extraction (Express, Flask, Django, FastAPI, Spring, etc.)
    for fpath, content in file_contents.items():
        for route in _extract_routes_from_content(fpath, content):
            key = (route["method"], route["path"])
            if key not in seen_global:
                seen_global.add(key)
                repo_routes.append(route)

    # Next.js file-based routing (App Router + Pages Router)
    for route in _extract_nextjs_routes(scan_root):
        key = (route["method"], route["path"])
        if key not in seen_global:
            seen_global.add(key)
            repo_routes.append(route)

    # ── Step 2: fetch Maeris API registry ────────────────────────────────────
    maeris_apis = await client.get_all_apis()

    issues: list[DriftIssue] = []

    # ── Step 3: ROUTE_MISSING — Maeris API has no code counterpart ───────────
    for api in maeris_apis:
        m_url = api.get("url") or api.get("endpoint") or ""
        m_method = (api.get("type") or api.get("method") or "*").upper()
        api_id = str(
            api.get("api_id") or api.get("id") or api.get("_id") or ""
        )
        api_name = api.get("name") or api.get("api_name") or m_url

        if not m_url:
            continue

        matched = any(
            _paths_match(r["path"], r["method"], m_url, m_method)
            for r in repo_routes
        )
        if not matched:
            issues.append(DriftIssue(
                kind=DriftKind.ROUTE_MISSING,
                severity=DriftSeverity.HIGH,
                description=(
                    f"Maeris API '{api_name}' ({m_method} {_normalize_path(m_url)}) "
                    f"has no matching route in the codebase."
                ),
                resource_type="api",
                resource_id=api_id,
                resource_name=api_name,
                detail={
                    "maeris_url": m_url,
                    "maeris_method": m_method,
                    "normalized_path": _normalize_path(m_url),
                    "scan_root": str(scan_root),
                },
            ))

    # ── Step 4: ROUTE_UNREGISTERED — code route not in Maeris ────────────────
    for route in repo_routes:
        matched = any(
            _paths_match(route["path"], route["method"],
                         api.get("url") or api.get("endpoint") or "",
                         (api.get("type") or api.get("method") or "*").upper())
            for api in maeris_apis
        )
        if not matched:
            issues.append(DriftIssue(
                kind=DriftKind.ROUTE_UNREGISTERED,
                severity=DriftSeverity.MEDIUM,
                description=(
                    f"Route {route['method']} {route['path']} found in "
                    f"'{route['file']}' is not registered in Maeris."
                ),
                resource_type="api",
                resource_id="",
                resource_name=f"{route['method']} {route['path']}",
                detail={
                    "method": route["method"],
                    "path": route["path"],
                    "file": route["file"],
                },
            ))

    return DriftReport(
        domain="api",
        issues=issues,
        meta={
            "scan_root": str(scan_root),
            "files_scanned": len(abs_paths),
            "routes_found": len(repo_routes),
            "maeris_apis": len(maeris_apis),
        },
    )
