"""Preview adapters — framework-specific build and route enumeration.

``start_dev_server`` has been removed. Previews are now served by CF Pages branch
deployments; the backend no longer runs local dev-server processes.

Each adapter implements:
  - install_deps   — install npm/yarn packages
  - build          — produce a deployable artifact directory
  - enumerate_routes — static analysis of source files for route manifest
"""
import asyncio
import os
from datetime import datetime, timezone
from typing import Protocol, runtime_checkable


@runtime_checkable
class PreviewAdapter(Protocol):
    adapter_name: str

    async def install_deps(self, repo_path: str) -> str: ...
    async def build(self, artifact_path: str) -> str: ...
    async def enumerate_routes(self, artifact_path: str) -> dict: ...


class ReactAdapter:
    adapter_name = "react_adapter"

    async def install_deps(self, repo_path: str) -> str:
        proc = await asyncio.create_subprocess_shell(
            "test -f package-lock.json && npm ci", cwd=repo_path,
            stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
        )
        await proc.communicate()
        return repo_path

    async def build(self, artifact_path: str) -> str:
        proc = await asyncio.create_subprocess_shell(
            "npm run build", cwd=artifact_path,
            stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
        )
        await proc.communicate()
        return os.path.join(artifact_path, "dist")

    async def enumerate_routes(self, artifact_path: str) -> dict:
        routes = [{"path": "/", "label": "Home", "type": "page"}]
        pages_dir = os.path.join(artifact_path, "src", "pages")
        if os.path.isdir(pages_dir):
            for f in os.listdir(pages_dir):
                if f.endswith((".tsx", ".jsx", ".ts", ".js")) and f not in ("Home.tsx", "index.tsx"):
                    name = f.rsplit(".", 1)[0]
                    routes.append({"path": f"/{name.lower()}", "label": name, "type": "page"})
        return {"routes": routes, "generated_at": datetime.now(timezone.utc).isoformat(), "source": self.adapter_name}


class NuxtAdapter:
    adapter_name = "nuxt_adapter"

    async def install_deps(self, repo_path: str) -> str:
        proc = await asyncio.create_subprocess_shell(
            "test -f package-lock.json && npm ci", cwd=repo_path,
            stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
        )
        await proc.communicate()
        return repo_path

    async def build(self, artifact_path: str) -> str:
        # `nuxi generate` produces static output at .output/public (CF Pages compatible).
        proc = await asyncio.create_subprocess_shell(
            "npx nuxi generate", cwd=artifact_path,
            stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
        )
        await proc.communicate()
        return os.path.join(artifact_path, ".output", "public")

    async def enumerate_routes(self, artifact_path: str) -> dict:
        routes = []
        pages_dir = os.path.join(artifact_path, "pages")
        if os.path.isdir(pages_dir):
            for f in os.listdir(pages_dir):
                if f.endswith(".vue"):
                    name = f.rsplit(".", 1)[0]
                    path = "/" if name == "index" else f"/{name}"
                    label = "Home" if name == "index" else name.capitalize()
                    routes.append({"path": path, "label": label, "type": "page"})
        if not routes:
            routes = [{"path": "/", "label": "Home", "type": "page"}]
        return {"routes": routes, "generated_at": datetime.now(timezone.utc).isoformat(), "source": self.adapter_name}


class HTMLAdapter:
    adapter_name = "html_adapter"

    async def install_deps(self, repo_path: str) -> str:
        return repo_path

    async def build(self, artifact_path: str) -> str:
        return artifact_path

    async def enumerate_routes(self, artifact_path: str) -> dict:
        routes = []
        if os.path.isdir(artifact_path):
            for f in os.listdir(artifact_path):
                if f.endswith(".html"):
                    name = f.rsplit(".", 1)[0]
                    path = "/" if name == "index" else f"/{name}.html"
                    label = "Home" if name == "index" else name.capitalize()
                    routes.append({"path": path, "label": label, "type": "page"})
        if not routes:
            routes = [{"path": "/", "label": "Home", "type": "page"}]
        return {"routes": routes, "generated_at": datetime.now(timezone.utc).isoformat(), "source": self.adapter_name}


def get_adapter(project_type: str) -> PreviewAdapter:
    adapters: dict[str, PreviewAdapter] = {
        "react_app": ReactAdapter(),
        "nuxt_app": NuxtAdapter(),
        "html": HTMLAdapter(),
    }
    adapter = adapters.get(project_type)
    if not adapter:
        raise ValueError(f"No preview adapter for project type: {project_type!r}")
    return adapter
