"""Synchronous HTTP client for Grukk to talk to the Peon REST API.

Modeled after the ``_http_request()`` pattern in ``loop.py`` but wrapped
in a class so each Grukk component can share a single configured client.
"""

from __future__ import annotations

import json
import logging
import urllib.error
import urllib.parse
import urllib.request
from typing import Any

log = logging.getLogger("peon-grukk")

# Track consecutive failures per URL to suppress log spam.
_failure_counts: dict[str, int] = {}
_SUPPRESS_AFTER = 5


class GrukkAPIClient:
    """Synchronous HTTP client for the Peon REST API.

    Parameters
    ----------
    api_url:
        Base URL for the Peon REST API (e.g. ``http://127.0.0.1:8420``).
    api_key:
        Optional API key for ``X-API-Key`` authentication.
    """

    def __init__(self, api_url: str, api_key: str = "") -> None:
        self.api_url = api_url.rstrip("/")
        self.api_key = api_key

    # ------------------------------------------------------------------
    # Core request helper
    # ------------------------------------------------------------------

    def _request(
        self,
        method: str,
        path: str,
        payload: dict | None = None,
        timeout: int = 5,
        *,
        quiet_statuses: set[int] | None = None,
    ) -> dict | list | None:
        """Make a synchronous HTTP request, returning parsed JSON or *None* on failure.

        Parameters
        ----------
        method:
            HTTP verb (GET, POST, PUT, PATCH, DELETE).
        path:
            API path **without** the base URL (e.g. ``/api/loops``).
        payload:
            Optional JSON body.
        timeout:
            Request timeout in seconds.
        quiet_statuses:
            HTTP status codes that should not produce a warning log.
        """
        url = f"{self.api_url}{path}"
        body = json.dumps(payload).encode("utf-8") if payload is not None else None
        headers: dict[str, str] = {"Content-Type": "application/json"}
        if self.api_key:
            headers["X-API-Key"] = self.api_key

        req = urllib.request.Request(url, data=body, headers=headers, method=method)
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                data = json.loads(resp.read())
                _failure_counts.pop(url, None)
                return data
        except urllib.error.HTTPError as exc:
            if quiet_statuses and exc.code in quiet_statuses:
                return None
            _failure_counts[url] = _failure_counts.get(url, 0) + 1
            if _failure_counts[url] <= _SUPPRESS_AFTER:
                log.warning("API call failed (%s %s): HTTP %d", method, url, exc.code)
            elif _failure_counts[url] == _SUPPRESS_AFTER + 1:
                log.warning(
                    "API call failed (%s %s): HTTP %d (suppressing further warnings)",
                    method, url, exc.code,
                )
            return None
        except (urllib.error.URLError, json.JSONDecodeError, OSError) as exc:
            _failure_counts[url] = _failure_counts.get(url, 0) + 1
            if _failure_counts[url] <= _SUPPRESS_AFTER:
                log.warning("API call failed (%s %s): %s", method, url, exc)
            elif _failure_counts[url] == _SUPPRESS_AFTER + 1:
                log.warning(
                    "API call failed (%s %s): %s (suppressing further warnings)",
                    method, url, exc,
                )
            return None

    # ------------------------------------------------------------------
    # Grukk messages
    # ------------------------------------------------------------------

    def get_pending_messages(self) -> list[dict]:
        """Fetch unanswered King messages (``GET /api/grukk/messages/pending``)."""
        result = self._request("GET", "/api/grukk/messages/pending")
        if isinstance(result, list):
            return result
        return []

    def send_message(
        self,
        content: str,
        user_id: int | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> dict | None:
        """Post a Grukk message (``POST /api/grukk/messages``)."""
        payload: dict[str, Any] = {
            "role": "grukk",
            "content": content,
            "metadata": metadata or {},
        }
        if user_id is not None:
            payload["user_id"] = user_id
        return self._request("POST", "/api/grukk/messages", payload)

    # ------------------------------------------------------------------
    # Loop heartbeats
    # ------------------------------------------------------------------

    def get_active_loops(self) -> list[dict]:
        """Fetch active peon-loop heartbeats (``GET /api/loops``)."""
        result = self._request("GET", "/api/loops")
        if isinstance(result, dict):
            return result.get("loops", [])
        return []

    # ------------------------------------------------------------------
    # Directives
    # ------------------------------------------------------------------

    def create_directive(
        self,
        loop_id: str,
        directive_type: str,
        payload: dict[str, Any] | None = None,
    ) -> dict | None:
        """Issue a directive to a peon-loop (``POST /api/loops/{loop_id}/directives``)."""
        body: dict[str, Any] = {
            "directive_type": directive_type,
            "payload": payload or {},
            "created_by": "grukk",
        }
        return self._request("POST", f"/api/loops/{loop_id}/directives", body)

    # ------------------------------------------------------------------
    # Tasks
    # ------------------------------------------------------------------

    def create_task(
        self,
        project_id: str,
        title: str,
        description: str,
        priority: str = "medium",
        feature_id: int | None = None,
    ) -> dict | None:
        """Create a new task (``POST /api/projects/{project_id}/tasks``)."""
        payload: dict[str, Any] = {
            "title": title,
            "description": description,
            "priority": priority,
        }
        if feature_id is not None:
            payload["feature_id"] = feature_id
        return self._request("POST", f"/api/projects/{project_id}/tasks", payload)

    def list_tasks(self, project_id: str, status: str | None = None) -> list[dict]:
        """List tasks for a project (``GET /api/projects/{project_id}/tasks``)."""
        path = f"/api/projects/{project_id}/tasks"
        if status:
            path += f"?status={urllib.parse.quote(status)}"
        result = self._request("GET", path)
        if isinstance(result, dict):
            return result.get("items", [])
        if isinstance(result, list):
            return result
        return []

    # ------------------------------------------------------------------
    # Features
    # ------------------------------------------------------------------

    def create_feature(
        self,
        project_id: str,
        name: str,
        description: str,
    ) -> dict | None:
        """Create a new feature (``POST /api/projects/{project_id}/features``)."""
        payload: dict[str, Any] = {
            "name": name,
            "description": description,
        }
        return self._request("POST", f"/api/projects/{project_id}/features", payload)

    def list_features(self, project_id: str, status: str | None = None) -> list[dict]:
        """List features for a project (``GET /api/projects/{project_id}/features``)."""
        path = f"/api/projects/{project_id}/features"
        if status:
            path += f"?status={urllib.parse.quote(status)}"
        result = self._request("GET", path)
        if isinstance(result, dict):
            return result.get("items", [])
        if isinstance(result, list):
            return result
        return []

    # ------------------------------------------------------------------
    # Approvals (placeholder — no approval API exists yet)
    # ------------------------------------------------------------------

    def get_pending_approvals(self) -> list[dict]:
        """Fetch pending approval requests.

        NOTE: The approvals API does not exist yet.  This will return an empty
        list until the approvals endpoints are created.
        """
        result = self._request(
            "GET", "/api/approvals", quiet_statuses={404},
        )
        if isinstance(result, list):
            return result
        return []

    def get_resolved_approvals(self, limit: int = 20) -> list[dict]:
        """Fetch recently resolved approvals for precedent matching.

        NOTE: The approvals API does not exist yet.
        """
        result = self._request(
            "GET",
            f"/api/approvals?status=resolved&limit={limit}",
            quiet_statuses={404},
        )
        if isinstance(result, list):
            return result
        return []

    # ------------------------------------------------------------------
    # PR conflict scanning
    # ------------------------------------------------------------------

    def scan_pr_conflicts(self, project_id: str) -> dict | None:
        """Trigger a PR conflict scan for a project. Returns scan summary."""
        return self._request(
            "POST",
            f"/api/projects/{project_id}/scan-pr-conflicts",
            timeout=60,
        )

    def resolve_approval(
        self, approval_id: str, status: str, response: str,
    ) -> dict | None:
        """Resolve a pending approval (approve/reject).

        NOTE: The approvals API does not exist yet.
        """
        payload = {"status": status, "response": response}
        return self._request(
            "PUT",
            f"/api/approvals/{approval_id}",
            payload,
            quiet_statuses={404},
        )
