"""API Client - wrapper for HTTP requests to FastAPI server."""
from typing import Any

import requests
from flask import has_request_context, request, session

from config import Config


class APIClient:
    """HTTP client for TieMeasureFlow API server."""

    def __init__(self):
        self.base_url = Config.API_SERVER_URL.rstrip("/")
        self.timeout = 30

    @staticmethod
    def _real_client_ip() -> str | None:
        """Best-effort tablet IP for downstream rate limiting.

        Behind Nginx + ProxyFix, ``request.remote_addr`` already resolves to
        the tablet IP. Outside a request context (background tasks, tests),
        return None so we don't forge a bogus header.
        """
        if not has_request_context():
            return None
        return request.remote_addr

    @property
    def _headers(self) -> dict[str, str]:
        """Build request headers with API key + real client IP from session."""
        headers = {"Content-Type": "application/json"}
        api_key = session.get("api_key")
        if api_key:
            headers["X-API-Key"] = api_key
        client_ip = self._real_client_ip()
        if client_ip:
            headers["X-Forwarded-For"] = client_ip
            headers["X-Real-IP"] = client_ip
        return headers

    def _handle_response(self, response: requests.Response) -> dict[str, Any]:
        """Parse response and return normalized dict.

        Returns:
            - If 2xx and not 204: response JSON
            - If 204 No Content: empty dict
            - If 4xx/5xx: {"error": True, "status_code": ..., "detail": "..."}
        """
        if response.ok:
            if response.status_code == 204:
                return {}
            return response.json()

        # Non-OK response (4xx/5xx)
        try:
            error_body = response.json()
            detail = error_body.get("detail", str(error_body))
            # FastAPI 422 returns detail as a list of validation errors:
            # [{"type": ..., "loc": ["body", "password"], "msg": "...", ...}, ...]
            if isinstance(detail, list):
                parts = []
                for e in detail:
                    if not isinstance(e, dict):
                        parts.append(str(e))
                        continue
                    msg = e.get("msg", str(e))
                    loc = e.get("loc") or []
                    # Strip leading "body"/"query"/"path" segments; keep field path.
                    field_path = [str(p) for p in loc if p not in ("body", "query", "path")]
                    field = ".".join(field_path) if field_path else None
                    parts.append(f"{field}: {msg}" if field else msg)
                detail = "; ".join(parts) or str(detail)
        except Exception:
            detail = response.text or f"HTTP {response.status_code}"

        return {
            "error": True,
            "status_code": response.status_code,
            "detail": detail
        }

    def get(self, endpoint: str, params: dict | None = None) -> dict[str, Any]:
        """GET request to API server."""
        try:
            response = requests.get(
                f"{self.base_url}{endpoint}",
                headers=self._headers,
                params=params,
                timeout=self.timeout,
            )
            return self._handle_response(response)
        except (requests.ConnectionError, requests.Timeout) as e:
            return {
                "error": True,
                "status_code": 0,
                "detail": f"Errore di connessione al server: {str(e)}"
            }

    def post(self, endpoint: str, data: dict | None = None, files: dict | None = None) -> dict[str, Any]:
        """POST request to API server."""
        try:
            if files:
                headers = {"X-API-Key": session.get("api_key", "")}
                client_ip = self._real_client_ip()
                if client_ip:
                    headers["X-Forwarded-For"] = client_ip
                    headers["X-Real-IP"] = client_ip
                response = requests.post(
                    f"{self.base_url}{endpoint}",
                    headers=headers,
                    data=data,
                    files=files,
                    timeout=self.timeout,
                )
            else:
                response = requests.post(
                    f"{self.base_url}{endpoint}",
                    headers=self._headers,
                    json=data,
                    timeout=self.timeout,
                )
            return self._handle_response(response)
        except (requests.ConnectionError, requests.Timeout) as e:
            return {
                "error": True,
                "status_code": 0,
                "detail": f"Errore di connessione al server: {str(e)}"
            }

    def put(self, endpoint: str, data: dict | None = None) -> dict[str, Any]:
        """PUT request to API server."""
        try:
            response = requests.put(
                f"{self.base_url}{endpoint}",
                headers=self._headers,
                json=data,
                timeout=self.timeout,
            )
            return self._handle_response(response)
        except (requests.ConnectionError, requests.Timeout) as e:
            return {
                "error": True,
                "status_code": 0,
                "detail": f"Errore di connessione al server: {str(e)}"
            }

    def delete(self, endpoint: str) -> dict[str, Any]:
        """DELETE request to API server."""
        try:
            response = requests.delete(
                f"{self.base_url}{endpoint}",
                headers=self._headers,
                timeout=self.timeout,
            )
            return self._handle_response(response)
        except (requests.ConnectionError, requests.Timeout) as e:
            return {
                "error": True,
                "status_code": 0,
                "detail": f"Errore di connessione al server: {str(e)}"
            }

    # --- Domain helpers ---

    def get_station_recipes(self, station_code: str) -> dict[str, Any]:
        """Return the list of active recipes assigned to the given station."""
        return self.get(f"/api/stations/by-code/{station_code}/recipes")


api_client = APIClient()
