"""SealVera client — handles auth, HTTP, async fire-and-forget."""

import os
import uuid
import json
import threading
import atexit
from datetime import datetime, timezone
from typing import Optional, List, Dict, Any
from concurrent.futures import ThreadPoolExecutor

try:
    import urllib.request as _urllib
    import urllib.error as _urllib_error
except ImportError:
    pass

VALID_DECISIONS = {
    # Task / agent
    "COMPLETED", "RESPONDED", "FAILED", "ERROR", "ESCALATED", "SKIPPED", "PARTIAL",
    # Tests
    "PASSED", "SUCCESS", "BLOCKED",
    # Gates
    "APPROVED", "REJECTED", "FLAGGED",
}

DEFAULT_ENDPOINT = "https://app.sealvera.com"


class SealVeraClient:
    def __init__(self):
        self._api_key: Optional[str] = None
        self._agent: str = "python-agent"
        self._endpoint: str = DEFAULT_ENDPOINT
        self._timeout: float = 5.0
        self._executor: Optional[ThreadPoolExecutor] = None
        self._pending: List = []
        self._lock = threading.Lock()
        self._configured = False

    def configure(
        self,
        api_key: str = None,
        agent: str = None,
        endpoint: str = None,
        auto_flush: bool = True,
        timeout: float = 5.0,
    ):
        self._api_key = api_key or os.environ.get("SEALVERA_API_KEY", "")
        self._agent   = agent    or os.environ.get("SEALVERA_AGENT", "python-agent")
        self._endpoint = (endpoint or os.environ.get("SEALVERA_ENDPOINT", DEFAULT_ENDPOINT)).rstrip("/")
        self._timeout  = timeout
        self._executor = ThreadPoolExecutor(max_workers=4, thread_name_prefix="sealvera")
        self._configured = True

        if auto_flush:
            atexit.register(self.flush)

    def _ensure_configured(self):
        if not self._configured:
            # Auto-configure from env vars — zero-config path
            self.configure()

    def _post(self, payload: dict) -> dict:
        """Blocking HTTP POST to ingest endpoint."""
        url  = f"{self._endpoint}/api/ingest"
        body = json.dumps(payload).encode("utf-8")
        req  = _urllib.Request(
            url,
            data=body,
            headers={
                "Content-Type":    "application/json",
                "x-sealvera-key":  self._api_key or "",
                "Content-Length":  str(len(body)),
                "User-Agent":      "sealvera-python/0.1.0",
            },
            method="POST",
        )
        try:
            with _urllib.urlopen(req, timeout=self._timeout) as resp:
                return json.loads(resp.read().decode("utf-8"))
        except _urllib_error.HTTPError as e:
            body_text = e.read().decode("utf-8", errors="replace")
            raise RuntimeError(f"SealVera ingest error {e.code}: {body_text}") from e
        except Exception as e:
            raise RuntimeError(f"SealVera request failed: {e}") from e

    def _build_payload(
        self,
        action: str,
        decision: str,
        input: dict,
        output: dict,
        reasoning: list,
        agent: str = None,
        trace_id: str = None,
        trace_name: str = None,
        model: str = None,
        timestamp: str = None,
        id: str = None,
    ) -> dict:
        if decision not in VALID_DECISIONS:
            import warnings
            warnings.warn(
                f"[sealvera] Unknown decision '{decision}'. "
                f"Valid values: {', '.join(sorted(VALID_DECISIONS))}",
                stacklevel=3,
            )

        payload = {
            "id":        id or str(uuid.uuid4()),
            "timestamp": timestamp or datetime.now(timezone.utc).isoformat(),
            "agent":     agent or self._agent,
            "action":    action,
            "decision":  decision,
            "input":     input  if isinstance(input,  dict) else {"value": str(input)},
            "output":    output if isinstance(output, dict) else {"value": str(output)},
            "reasoning_steps": reasoning or [],
        }
        if trace_id:   payload["traceId"]    = trace_id
        if trace_name: payload["traceName"]  = trace_name
        if model:      payload["model"]      = model
        return payload

    def log(
        self,
        action: str,
        decision: str,
        input: dict = None,
        output: dict = None,
        reasoning: list = None,
        agent: str = None,
        trace_id: str = None,
        trace_name: str = None,
        model: str = None,
        timestamp: str = None,
        id: str = None,
        blocking: bool = False,
    ) -> dict:
        self._ensure_configured()
        payload = self._build_payload(
            action=action, decision=decision,
            input=input or {}, output=output or {}, reasoning=reasoning or [],
            agent=agent, trace_id=trace_id, trace_name=trace_name,
            model=model, timestamp=timestamp, id=id,
        )

        if blocking:
            try:
                return self._post(payload)
            except Exception as e:
                print(f"[sealvera] log error (non-fatal): {e}")
                return {}

        # Fire and forget via thread pool
        future = self._executor.submit(self._post_safe, payload)
        with self._lock:
            self._pending.append(future)
        return {"ok": True, "id": payload["id"]}

    def _post_safe(self, payload: dict):
        try:
            result = self._post(payload)
            return result
        except Exception as e:
            print(f"[sealvera] log error (non-fatal): {e}")
            return {}

    def flush(self):
        """Wait for all in-flight requests to complete."""
        with self._lock:
            pending = list(self._pending)
            self._pending.clear()
        for future in pending:
            try:
                future.result(timeout=10)
            except Exception:
                pass


# Module-level default client — used by sealvera.log(), sealvera.trace() etc.
_default_client = SealVeraClient()
