"""VS Code Web / code-server adapter."""
from __future__ import annotations

import asyncio
import shutil
from pathlib import Path

import structlog

from proxym.tools.adapters._base import BaseAdapter
from proxym.tools.adapters._result import ToolResult

logger = structlog.get_logger()


class VSCodeAdapter(BaseAdapter):
    """Adapter for VS Code Web / code-server (starts container, sends task via API)."""

    def build_args(
        self, prompt: str, project_dir: str, mode: str = "", model: str = "",
        files: list[str] | None = None,
    ) -> list[str]:
        # VS Code Web is started via docker compose, not a direct CLI call
        return ["docker", "compose", "--profile", "vscode", "up", "-d", "vscode"]

    async def run(
        self,
        prompt: str,
        project_dir: str,
        ticket_id: str = "",
        mode: str = "",
        model: str = "",
        files: list[str] | None = None,
        timeout: float = 120,
    ) -> ToolResult:
        """Start VS Code Web container and verify it's running."""
        import time

        logger.info("vscode_adapter_run", ticket_id=ticket_id, project_dir=project_dir)
        t0 = time.monotonic()

        # Check docker is available before attempting
        if not shutil.which("docker"):
            duration = time.monotonic() - t0
            logger.warning("vscode_adapter_no_docker", ticket_id=ticket_id)
            return ToolResult(
                tool=self.tool.name, ticket_id=ticket_id,
                exit_code=127,
                stderr=(
                    "docker not found on PATH. VS Code Web requires docker compose. "
                    "Run from host: docker compose --profile vscode up -d"
                ),
                duration_s=duration,
            )

        # Find docker-compose.yml directory
        compose_dir = self._find_compose_dir(project_dir)
        args = self.build_args(prompt, project_dir, mode, model, files)

        try:
            proc = await asyncio.create_subprocess_exec(
                *args,
                cwd=compose_dir,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
            stdout_b, stderr_b = await asyncio.wait_for(
                proc.communicate(), timeout=timeout,
            )
            duration = time.monotonic() - t0
            stdout = stdout_b.decode(errors="replace")
            stderr = stderr_b.decode(errors="replace")

            if proc.returncode == 0:
                stdout += f"\n[proxym] VS Code Web started for ticket {ticket_id}"
                stdout += f"\n[proxym] Prompt: {prompt[:200]}"

            return ToolResult(
                tool=self.tool.name, ticket_id=ticket_id,
                exit_code=proc.returncode or 0,
                stdout=stdout, stderr=stderr, duration_s=duration,
            )
        except asyncio.TimeoutError:
            duration = time.monotonic() - t0
            return ToolResult(
                tool=self.tool.name, ticket_id=ticket_id,
                exit_code=-1, stderr=f"Timeout after {timeout}s", duration_s=duration,
            )
        except FileNotFoundError:
            duration = time.monotonic() - t0
            return ToolResult(
                tool=self.tool.name, ticket_id=ticket_id,
                exit_code=127,
                stderr="docker binary not found. Run from host: docker compose --profile vscode up -d",
                duration_s=duration,
            )
        except Exception as exc:
            duration = time.monotonic() - t0
            return ToolResult(
                tool=self.tool.name, ticket_id=ticket_id,
                exit_code=-1, stderr=str(exc), duration_s=duration,
            )

    def _find_compose_dir(self, project_dir: str) -> str:
        for candidate in [
            Path(project_dir),
            Path(project_dir).parent,
            Path(__file__).resolve().parent.parent.parent.parent,  # repo root
            Path("/app"),
        ]:
            if (candidate / "docker-compose.yml").exists():
                return str(candidate)
        return project_dir
