"""HTTPS client for the Moon ERP cloud LIS API (stdlib urllib only)."""

from __future__ import annotations

import json
import logging
import ssl
import urllib.error
import urllib.request
from typing import Any, Dict, List, Optional

log = logging.getLogger("cloud")


class CloudError(Exception):
    """Raised on a non-2xx response or transport failure."""

    def __init__(self, message: str, status: Optional[int] = None) -> None:
        super().__init__(message)
        self.status = status


class CloudClient:
    """Thin wrapper over the LIS endpoints the middleware needs."""

    def __init__(self, config) -> None:
        self._cfg = config
        self._token = config.token
        self._ctx = ssl.create_default_context()
        if not config.verify_ssl:
            self._ctx.check_hostname = False
            self._ctx.verify_mode = ssl.CERT_NONE

    # ----- auth -----------------------------------------------------------
    def ensure_token(self) -> None:
        """Acquire a token by login if configured and none is set."""
        if self._token:
            return
        if not self._cfg.login_enabled:
            raise CloudError("no token and login disabled")
        resp = self._request(
            "POST",
            "/auth/login",
            {"email": self._cfg.login_email, "password": self._cfg.login_password},
            auth=False,
        )
        token = resp.get("token") or (resp.get("data") or {}).get("token")
        if not token:
            raise CloudError("login succeeded but no token in response")
        self._token = token
        log.info("acquired auth token via login")

    @property
    def has_token(self) -> bool:
        return bool(self._token)

    def test(self) -> Dict[str, Any]:
        """Verify the cloud is reachable and the credentials are valid.

        Used by the admin UI 'Test connection' button — never raises.
        """
        try:
            self.ensure_token()
            me = self._request("GET", "/auth/me", None)
            user = (me.get("data") if isinstance(me, dict) else None) or me or {}
            who = user.get("name") or user.get("email") or "service account"
            return {"ok": True, "message": f"Connected as {who}"}
        except CloudError as exc:
            return {"ok": False, "message": str(exc), "status": exc.status}

    # ----- endpoints ------------------------------------------------------
    def post_result(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """POST /lis/machine-results — ingest one parameter."""
        return self._request("POST", "/lis/machine-results", payload)

    def heartbeat(self, machine_id: int, workstation: str = "") -> None:
        """POST /lis/machines/{id}/heartbeat — keep the device 'online'.

        Sends the workstation name so the cloud can record which station the
        machine was last seen on (ignored harmlessly by older backends).
        """
        body = {"workstation": workstation} if workstation else {}
        self._request("POST", f"/lis/machines/{machine_id}/heartbeat", body)

    def work_orders(self, machine_id: int) -> List[Dict[str, Any]]:
        """GET /lis/machines/{id}/work-orders — pending orders for this machine."""
        resp = self._request("GET", f"/lis/machines/{machine_id}/work-orders", None)
        data = resp.get("data") if isinstance(resp, dict) else None
        return data or []

    def orders_for(self, machine_id: int, barcode: str) -> List[str]:
        """GET /lis/machines/{id}/orders?barcode= — analyzer test codes ordered
        for a tube (bidirectional ASTM host query)."""
        import urllib.parse
        bc = urllib.parse.quote(str(barcode), safe="")
        resp = self._request("GET", f"/lis/machines/{machine_id}/orders?barcode={bc}", None)
        data = resp.get("data") if isinstance(resp, dict) else None
        return [str(c) for c in (data or [])]

    def log_communication(self, machine_id: int, raw: str, summary: str = "",
                          protocol: str = "hl7", direction: str = "incoming") -> None:
        """POST /lis/machines/{id}/communication-logs — archive a full raw
        message (HL7/ASTM) for audit."""
        self._request("POST", f"/lis/machines/{machine_id}/communication-logs", {
            "raw_data": raw, "parsed_summary": summary,
            "protocol": protocol, "direction": direction,
        })

    def list_machines(self) -> List[Dict[str, Any]]:
        """GET /lis/machines — the cloud-managed analyzer list (with
        connection_settings the middleware uses to build its listeners)."""
        self.ensure_token()
        resp = self._request("GET", "/lis/machines", None)
        data = resp.get("data") if isinstance(resp, dict) else None
        return data or []

    # ----- transport ------------------------------------------------------
    def _request(
        self, method: str, path: str, body: Optional[Dict[str, Any]], auth: bool = True
    ) -> Dict[str, Any]:
        url = self._cfg.base_url + path
        data = json.dumps(body).encode("utf-8") if body is not None else None
        req = urllib.request.Request(url, data=data, method=method)
        req.add_header("Accept", "application/json")
        if data is not None:
            req.add_header("Content-Type", "application/json")
        if auth and self._token:
            req.add_header(self._cfg.auth_header, f"Bearer {self._token}")
        try:
            with urllib.request.urlopen(
                req, timeout=self._cfg.http_timeout, context=self._ctx
            ) as resp:
                raw = resp.read().decode("utf-8", errors="replace")
                return json.loads(raw) if raw else {}
        except urllib.error.HTTPError as exc:
            detail = exc.read().decode("utf-8", errors="replace")[:500]
            raise CloudError(f"HTTP {exc.code} {path}: {detail}", status=exc.code)
        except (urllib.error.URLError, OSError, TimeoutError) as exc:
            raise CloudError(f"transport error {path}: {exc}")
