"""Import scan. Candidates never activate routing."""

from __future__ import annotations

import re
from typing import Any

from src.domain.adopt.contract import PLATFORM_API_PREFIX, claims_reserved

_DESCRIPTION_NAMES = {
    "openapi.json",
    "openapi.yaml",
    "openapi.yml",
    "swagger.json",
    "swagger.yaml",
    "swagger.yml",
}
_SERVER_PREFIXES = (
    "server/",
    "src/server/",
    "functions/",
    "app/api/",
    "src/app/api/",
    "pages/api/",
    "src/pages/api/",
)
_NON_PORTABLE = (
    "node:fs",
    "node:child_process",
    "child_process",
    "better-sqlite3",
    "prisma",
    "mongoose",
    "from \"pg\"",
    "from 'pg'",
    "from \"mysql\"",
    "from 'mysql'",
    "require(\"sharp\")",
    "require('sharp')",
    ".node",
)
_SECRET_KEY = re.compile(r"(SECRET|TOKEN|PASSWORD|PRIVATE|CREDENTIAL)", re.I)
_URL = re.compile(r"https?://[^\s\"'`<>]+")
_ENV_ASSIGN = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)=(.*)$")


def scan_import(files: dict[str, str | None]) -> dict[str, Any]:
    """Return adopt detection. ``attachment_status`` is never ``active``."""
    signals: list[str] = []
    candidates: list[dict[str, Any]] = []
    reserved: list[str] = []
    non_portable: list[str] = []
    for path, text in files.items():
        normal = path.replace("\\", "/")
        while normal.startswith("./"):
            normal = normal[2:]
        normal = normal.lstrip("/")
        name = normal.rsplit("/", 1)[-1].lower()
        body = text or ""
        if _is_description(normal, name):
            signals.append("description_file")
            candidates.extend(_description_candidates(normal, body))
        if _is_server_surface(normal, body):
            signals.append("server_surface")
        if _browser_signal(normal, body):
            signals.append("browser_api")
            candidates.extend(_url_candidates(normal, body))
        if any(marker in body for marker in _NON_PORTABLE):
            non_portable.append(normal)
        reserved.extend(_reserved_claims(normal, body))
    unique_signals = list(dict.fromkeys(signals))
    detected = bool(unique_signals)
    return {
        "project_mode": "adopt" if detected else "owned",
        "has_external_api": detected,
        "attachment_status": "detected" if detected else None,
        "ask_user": detected,
        "routing_active": False,
        "signals": unique_signals,
        "candidates": _dedupe(candidates),
        "reserved_claims": list(dict.fromkeys(reserved)),
        "non_portable_files": non_portable,
    }


def projection_allowed(*, backend_source: str, backend_repo_available: bool, portable: bool) -> str:
    """Where a function may be projected from. Separate never falls back to the frontend repo."""
    if not portable:
        return "unavailable_until_external_endpoint"
    if backend_source == "co_located":
        return "frontend_repo"
    if backend_source == "separate" and backend_repo_available:
        return "backend_repo"
    return "unavailable_until_external_endpoint"


def _is_description(path: str, name: str) -> bool:
    return name in _DESCRIPTION_NAMES or path.endswith(".graphql") or path.endswith(".gql")


def _is_server_surface(path: str, body: str) -> bool:
    if any(path.startswith(prefix) or f"/{prefix}" in f"/{path}" for prefix in _SERVER_PREFIXES):
        return True
    if "getServerSideProps" in body:
        return True
    if path.rsplit("/", 1)[-1].startswith("nuxt.config") and re.search(r"ssr\s*:\s*true", body):
        return True
    return False


def _browser_signal(path: str, body: str) -> bool:
    if not body:
        return False
    if any(token in body for token in ("fetch(", "axios", "$fetch", "ofetch", "NUXT_PUBLIC_", "NEXT_PUBLIC_", "VITE_")):
        return True
    if any(token in body for token in ("openapi-generator", "openapi-fetch", "@openapitools", "orval")):
        return True
    if path.endswith(".env") or path.split("/")[-1].startswith(".env"):
        return any(_env_origin(line) for line in body.splitlines())
    return False


def _description_candidates(path: str, body: str) -> list[dict[str, Any]]:
    found = _url_candidates(path, body)
    if found:
        for item in found:
            item["kind"] = "description_file"
            item["activates_routing"] = False
        return found
    return [{
        "kind": "description_file",
        "source": path,
        "origin": None,
        "activates_routing": False,
    }]


def _url_candidates(path: str, body: str) -> list[dict[str, Any]]:
    found: list[dict[str, Any]] = []
    for line in body.splitlines():
        env = _ENV_ASSIGN.match(line.strip())
        if env and _SECRET_KEY.search(env.group(1)):
            found.append({
                "kind": "env",
                "source": path,
                "env_name": env.group(1),
                "origin": None,
                "origin_withheld": True,
                "activates_routing": False,
            })
            continue
        for match in _URL.findall(line):
            origin, withheld = _redact_url(match)
            found.append({
                "kind": "url",
                "source": path,
                "origin": origin,
                "origin_withheld": withheld,
                "activates_routing": False,
            })
    return found


def _env_origin(line: str) -> bool:
    match = _ENV_ASSIGN.match(line.strip())
    if not match:
        return False
    return bool(_URL.search(match.group(2)))


def _redact_url(value: str) -> tuple[str | None, bool]:
    if "@" in value.split("://", 1)[-1]:
        return None, True
    cleaned = value.rstrip(").,;\"'")
    return cleaned, False


def _reserved_claims(path: str, body: str) -> list[str]:
    claims: list[str] = []
    if claims_reserved("/" + path):
        claims.append(path)
    if PLATFORM_API_PREFIX in body:
        claims.append(path)
    return claims


def _dedupe(candidates: list[dict[str, Any]]) -> list[dict[str, Any]]:
    seen: set[tuple[Any, ...]] = set()
    unique: list[dict[str, Any]] = []
    for item in candidates:
        key = (item.get("kind"), item.get("source"), item.get("origin"), item.get("env_name"))
        if key in seen:
            continue
        seen.add(key)
        unique.append(item)
    return unique
