"""Neurvona REST client. Python 3.10+. No dependencies or automatic mutation retries."""
import json
import os
from urllib.error import HTTPError
from urllib.parse import quote, urlparse, urlencode
from urllib.request import Request, build_opener, HTTPRedirectHandler

class NeurvonaError(Exception):
    def __init__(self, status, error, retry_after=None):
        super().__init__(error.get("message", f"Neurvona returned HTTP {status}"))
        self.status, self.code = status, error.get("code", "unexpected_response")
        self.retry_after_seconds = int(retry_after) if retry_after else None
        # approval_required: give approval["url"] to the account owner, then send the same request again after they approve.
        self.approval, self.scope = error.get("approval"), error.get("scope")

class _NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

class Neurvona:
    def __init__(self, api_key=None, base_url="https://neurvona.ai/api/v1"):
        self.api_key = api_key or os.environ.get("NEURVONA_API_KEY")
        if not self.api_key:
            raise ValueError("Set NEURVONA_API_KEY.")
        url = urlparse(base_url)
        if url.scheme != "https" and url.hostname not in ("localhost", "127.0.0.1"):
            raise ValueError("Use HTTPS.")
        self.base_url = base_url.rstrip("/")
        self.opener = build_opener(_NoRedirect())

    def request(self, path, method="GET", body=None, idempotency_key=None):
        headers = {"Authorization": "Bearer " + self.api_key}
        if body is not None:
            headers["Content-Type"] = "application/json"
        if idempotency_key:
            headers["Idempotency-Key"] = idempotency_key
        request = Request(self.base_url + path, method=method, headers=headers,
                          data=None if body is None else json.dumps(body).encode())
        try:
            with self.opener.open(request, timeout=150) as response:
                return json.load(response)
        except HTTPError as error:
            try:
                problem = json.load(error).get("error", {})
            except (ValueError, AttributeError):
                problem = {"code": "unexpected_response"}
            raise NeurvonaError(error.code, problem, error.headers.get("Retry-After")) from error

    def me(self): return self.request("/me")
    def posts(self, body, idempotency_key):
        if not idempotency_key: raise ValueError("Pass a stable idempotency key.")
        return self.request("/posts", "POST", body, idempotency_key)
    def autopilot(self): return self.request("/autopilot")
    def update_autopilot(self, settings): return self.request("/autopilot", "PATCH", settings)
    def pause(self): return self.request("/autopilot/pause", "POST")
    def resume(self, *, consent): return self.request("/autopilot/resume", "POST", {"consent": consent})
    def activity(self, limit=20): return self.request("/activity?limit=" + quote(str(limit)))
    def boosts(self): return self.request("/boosts")
    def sources(self): return self.request("/persona/sources")
    def save_source(self, source): return self.request("/persona/sources", "POST", source)
    def remove_source(self, source_id): return self.request("/persona/sources?id=" + quote(source_id, safe=""), "DELETE")

    def capabilities(self): return self.request("/persona/capabilities")
    def jobs(self): return self.request("/persona/jobs")
    def job(self, job_id): return self.request("/persona/jobs/" + quote(job_id, safe=""))
    def start_job(self, body, idempotency_key):
        if not idempotency_key: raise ValueError("Pass a stable idempotency key.")
        return self.request("/persona/jobs", "POST", body, idempotency_key)
    def stop_job(self, job_id): return self.request("/persona/jobs/" + quote(job_id, safe=""), "DELETE")
    def connections(self): return self.request("/persona/connections")
    def update_connection(self, provider, action): return self.request("/persona/connections", "POST", {"provider": provider, "action": action})
    def control_boosts(self, body): return self.request("/boosts", "POST", body)
    def transcribe(self, data, *, content_type, max_charge_cents, consent, idempotency_key, title="Conversation"):
        if consent is not True or not idempotency_key: raise ValueError("Approve the price and provide a stable idempotency key.")
        if len(data) > 25 * 1024 * 1024: raise ValueError("Maximum upload is 25 MB.")
        query = urlencode({"maxChargeCents": max_charge_cents, "consent": "true", "title": title})
        request = Request(self.base_url + "/persona/transcriptions?" + query, data=data, method="POST", headers={"Authorization": "Bearer " + self.api_key, "Content-Type": content_type, "Idempotency-Key": idempotency_key})
        try:
            with self.opener.open(request, timeout=150) as response: return json.load(response)
        except HTTPError as error:
            raise NeurvonaError(error.code, json.load(error).get("error", {}), error.headers.get("Retry-After")) from error
    def recording(self, job_id):
        request = Request(self.base_url + "/persona/jobs/" + quote(job_id, safe="") + "/file", headers={"Authorization": "Bearer " + self.api_key})
        try:
            with self.opener.open(request, timeout=150) as response: return response.read()
        except HTTPError as error:
            raise NeurvonaError(error.code, json.load(error).get("error", {}), error.headers.get("Retry-After")) from error
