"""API client for web backend integration (System 2 <-> System 1).

Allows the local executor to:
- Fetch simulation plans from the web backend
- Upload scan results back to the web backend

All source is ASCII.
"""

from __future__ import annotations

import json
import os
from pathlib import Path
from typing import Any

import urllib.request
import urllib.error


class WebAPIClient:
    """Client for the web backend REST API."""

    def __init__(self, base_url: str = "http://127.0.0.1:8000"):
        self.base_url = base_url.rstrip("/")
        self.timeout = 30

    def _request(self, method: str, path: str, data: dict | None = None,
                 files: dict | None = None) -> Any:
        """Make an HTTP request to the API."""
        url = f"{self.base_url}{path}"
        if files:
            # Multipart form upload
            import uuid
            boundary = uuid.uuid4().hex
            body = b""
            for field_name, file_info in files.items():
                body += f"--{boundary}\r\n".encode()
                body += f'Content-Disposition: form-data; name="{field_name}"; filename="{file_info["filename"]}"\r\n'.encode()
                body += b"Content-Type: application/octet-stream\r\n\r\n"
                body += file_info["content"]
                body += b"\r\n"
            body += f"--{boundary}--\r\n".encode()
            req = urllib.request.Request(url, data=body, method=method)
            req.add_header("Content-Type", f"multipart/form-data; boundary={boundary}")
        else:
            body = json.dumps(data).encode() if data else None
            req = urllib.request.Request(url, data=body, method=method)
            req.add_header("Content-Type", "application/json")

        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as resp:
                return json.loads(resp.read())
        except urllib.error.HTTPError as e:
            error_body = e.read().decode()
            raise RuntimeError(f"API {method} {path} failed: {e.code} {error_body}")
        except urllib.error.URLError as e:
            raise RuntimeError(f"API connection failed: {e.reason}")

    # -- Health --

    def health_check(self) -> bool:
        """Check if the web backend is reachable."""
        try:
            result = self._request("GET", "/api/health")
            return result.get("status") == "ok"
        except Exception:
            return False

    # -- Projects --

    def list_projects(self, topology: str | None = None) -> list[dict]:
        """List all projects."""
        path = "/api/projects"
        if topology:
            path += f"?topology={topology}"
        result = self._request("GET", path)
        return result.get("items", [])

    def get_project(self, project_id: int) -> dict:
        """Get a project by ID."""
        return self._request("GET", f"/api/projects/{project_id}")

    # -- Plans --

    def list_plans(self, project_id: int | None = None) -> list[dict]:
        """List plans, optionally filtered by project."""
        path = "/api/plans"
        if project_id:
            path += f"?project_id={project_id}"
        result = self._request("GET", path)
        return result.get("items", [])

    def get_plan(self, plan_id: int) -> dict:
        """Get a plan by ID."""
        return self._request("GET", f"/api/plans/{plan_id}")

    def download_plan(self, plan_id: int) -> dict:
        """Download a plan as simulation_plan.json (compatible with local executor)."""
        return self._request("GET", f"/api/plans/{plan_id}/download")

    def download_plan_by_uuid(self, plan_uuid: str) -> dict:
        """Download a plan by its plan_id string (e.g. SP-20260827-001)."""
        return self._request("GET", f"/api/plans/by-plan-id/{plan_uuid}/download")

    def upload_results(self, plan_id: int, csv_path: str | Path) -> dict:
        """Upload scan_results.csv to the web backend."""
        csv_path = Path(csv_path)
        if not csv_path.exists():
            raise FileNotFoundError(f"CSV file not found: {csv_path}")
        with open(csv_path, "rb") as f:
            content = f.read()
        return self._request(
            "POST",
            f"/api/plans/{plan_id}/upload-results",
            files={"file": {"filename": csv_path.name, "content": content}},
        )

    def get_plan_results(self, plan_id: int) -> list[dict]:
        """Get all results for a plan."""
        result = self._request("GET", f"/api/plans/{plan_id}/results")
        return result.get("items", [])

    # -- Convenience: full workflow --

    def fetch_and_save_plan(self, plan_id: int, output_dir: str | Path) -> Path:
        """Download a plan and save it as simulation_plan.json.

        Args:
            plan_id: Plan ID in the web backend.
            output_dir: Directory to save the plan JSON.

        Returns:
            Path to the saved simulation_plan.json.
        """
        plan_data = self.download_plan(plan_id)
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)
        plan_uuid = plan_data.get("plan_id", "plan")
        output_path = output_dir / f"{plan_uuid}.json"
        with open(output_path, "w", encoding="utf-8") as f:
            json.dump(plan_data.get("plan_data", plan_data), f, indent=2, ensure_ascii=False)
        return output_path


def get_api_client() -> WebAPIClient:
    """Get API client with URL from environment variable or default."""
    base_url = os.environ.get("AFM_WEB_API_URL", "http://127.0.0.1:8000")
    return WebAPIClient(base_url)
