"""Lojiq Public API — thin Python client, generated from openapi.yaml 1.2.0.

Standard library only (urllib). One method per operation; bodies and responses are plain dicts.
See https://docs.lojiq.ai/reference/ for every field. NOT an openapi-generator build — run
tools/generate-sdks.sh for typed models.
"""
from __future__ import annotations

import json
import urllib.error
import urllib.parse
import urllib.request
from typing import Any, Optional

__version__ = "1.2.0"
DEFAULT_BASE_URL = "https://api.lojiq.ai/v1"


class LojiqError(Exception):
    def __init__(self, status: int, code: str, body: Any, request_id: Optional[str], message: Optional[str] = None):
        super().__init__(message or f"{status} {code}")
        self.status, self.code, self.body, self.request_id = status, code, body, request_id


class _NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):  # noqa: D401
        return None


class Lojiq:
    def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL, timeout: float = 30.0, user_agent: str = "lojiq-python/1.2.0"):
        if not api_key:
            raise ValueError("api_key is required")
        self.api_key, self.base_url, self.timeout, self.user_agent = api_key, base_url.rstrip("/"), timeout, user_agent

    def request(self, method: str, path: str, query: Optional[dict] = None, body: Any = None, kind: str = "json", auth: bool = True) -> Any:
        """kind: json | text | bytes | redirect (returns the Location header)."""
        url = self.base_url + path
        if query:
            q = {k: v for k, v in query.items() if v is not None}
            if q:
                url += "?" + urllib.parse.urlencode(q)
        headers = {"Accept": "application/json", "User-Agent": self.user_agent}
        if auth:
            headers["Authorization"] = f"Bearer {self.api_key}"
        data = None
        if body is not None:
            headers["Content-Type"] = "application/json"
            data = json.dumps(body).encode("utf-8")
        req = urllib.request.Request(url, data=data, method=method, headers=headers)
        opener = urllib.request.build_opener(_NoRedirect) if kind == "redirect" else urllib.request.build_opener()
        try:
            with opener.open(req, timeout=self.timeout) as res:
                request_id = res.headers.get("X-Request-Id")
                if kind == "redirect":
                    return res.headers.get("Location")
                raw = res.read()
                if kind == "text":
                    return raw.decode("utf-8")
                if kind == "bytes":
                    return raw
                return json.loads(raw) if raw else None
        except urllib.error.HTTPError as e:
            if kind == "redirect" and 300 <= e.code < 400:
                return e.headers.get("Location")
            raw = e.read()
            request_id = e.headers.get("X-Request-Id")
            try:
                parsed = json.loads(raw)
            except Exception:
                parsed = raw.decode("utf-8", "replace")
            code = parsed.get("error", f"http_{e.code}") if isinstance(parsed, dict) else f"http_{e.code}"
            msg = parsed.get("message") if isinstance(parsed, dict) else None
            raise LojiqError(e.code, code, parsed, request_id, f"{e.code} {code}: {msg}" if msg else None) from None

    def get_api_meta(self) -> Any:
        """API metadata — GET / (scope: none)."""
        return self.request("GET", "/")

    def list_event_catalog(self) -> Any:
        """List webhook event types — GET /event-catalog (scope: webhooks:manage)."""
        return self.request("GET", "/event-catalog")

    def get_account(self) -> Any:
        """Get your organization — GET /account (scope: account:read)."""
        return self.request("GET", "/account")

    def list_leads(self, limit: Optional[int] = None, offset: Optional[int] = None, campaign_id: Optional[str] = None, phone_number: Optional[str] = None, status: Optional[str] = None) -> Any:
        """List leads — GET /leads (scope: leads:read)."""
        return self.request("GET", "/leads", query={"limit": limit, "offset": offset, "campaign_id": campaign_id, "phone_number": phone_number, "status": status})

    def create_lead(self, body: dict) -> Any:
        """Create a lead — POST /leads (scope: leads:write)."""
        return self.request("POST", "/leads", body=body)

    def get_lead(self, id: str) -> Any:
        """Get a lead — GET /leads/{id} (scope: leads:read)."""
        return self.request("GET", f"/leads/{urllib.parse.quote(str(id), safe="")}")

    def update_lead(self, id: str, body: dict) -> Any:
        """Update a lead — PATCH /leads/{id} (scope: leads:write)."""
        return self.request("PATCH", f"/leads/{urllib.parse.quote(str(id), safe="")}", body=body)

    def bulk_import_leads(self, body: dict) -> Any:
        """Bulk import leads from CSV — POST /leads/bulk (scope: leads:write)."""
        return self.request("POST", "/leads/bulk", body=body)

    def inspect_leads_csv(self, body: dict) -> Any:
        """Preflight a CSV — POST /leads/inspect-csv (scope: leads:write)."""
        return self.request("POST", "/leads/inspect-csv", body=body)

    def export_leads_csv(self, campaign_id: Optional[str] = None) -> Any:
        """Export leads as CSV — GET /leads.csv (scope: leads:read)."""
        return self.request("GET", "/leads.csv", query={"campaign_id": campaign_id}, kind="text")

    def list_contacts(self, limit: Optional[int] = None, offset: Optional[int] = None) -> Any:
        """List contacts — GET /contacts (scope: contacts:read). NOT YET AVAILABLE on production."""
        return self.request("GET", "/contacts", query={"limit": limit, "offset": offset})

    def create_contact(self, body: dict) -> Any:
        """Create a contact — POST /contacts (scope: contacts:write). NOT YET AVAILABLE on production."""
        return self.request("POST", "/contacts", body=body)

    def list_campaigns(self, limit: Optional[int] = None, offset: Optional[int] = None, status: Optional[str] = None) -> Any:
        """List campaigns — GET /campaigns (scope: campaigns:read)."""
        return self.request("GET", "/campaigns", query={"limit": limit, "offset": offset, "status": status})

    def get_campaign(self, id: str) -> Any:
        """Get a campaign — GET /campaigns/{id} (scope: campaigns:read)."""
        return self.request("GET", f"/campaigns/{urllib.parse.quote(str(id), safe="")}")

    def start_campaign(self, id: str) -> Any:
        """Start (or resume) a campaign — POST /campaigns/{id}/start (scope: campaigns:write)."""
        return self.request("POST", f"/campaigns/{urllib.parse.quote(str(id), safe="")}/start")

    def stop_campaign(self, id: str) -> Any:
        """Pause a campaign — POST /campaigns/{id}/stop (scope: campaigns:write)."""
        return self.request("POST", f"/campaigns/{urllib.parse.quote(str(id), safe="")}/stop")

    def list_calls(self, limit: Optional[int] = None, offset: Optional[int] = None, campaign_id: Optional[str] = None, lead_id: Optional[str] = None) -> Any:
        """List calls — GET /calls (scope: calls:read)."""
        return self.request("GET", "/calls", query={"limit": limit, "offset": offset, "campaign_id": campaign_id, "lead_id": lead_id})

    def create_call(self, body: dict) -> Any:
        """Start an ad-hoc call — POST /calls (scope: calls:write)."""
        return self.request("POST", "/calls", body=body)

    def get_call(self, id: str) -> Any:
        """Get a call — GET /calls/{id} (scope: calls:read)."""
        return self.request("GET", f"/calls/{urllib.parse.quote(str(id), safe="")}")

    def get_call_transcript(self, id: str) -> Any:
        """Get a call transcript — GET /calls/{id}/transcript (scope: calls:read)."""
        return self.request("GET", f"/calls/{urllib.parse.quote(str(id), safe="")}/transcript")

    def hangup_call(self, id: str) -> Any:
        """End a live call — POST /calls/{id}/hangup (scope: calls:write)."""
        return self.request("POST", f"/calls/{urllib.parse.quote(str(id), safe="")}/hangup")

    def get_call_recording(self, id: str) -> Any:
        """Get a call recording — GET /calls/{id}/recording (scope: calls:read)."""
        return self.request("GET", f"/calls/{urllib.parse.quote(str(id), safe="")}/recording", kind="redirect")

    def list_agents(self, limit: Optional[int] = None, offset: Optional[int] = None, created_via: Optional[str] = None) -> Any:
        """List voice agents — GET /agents (scope: agents:read)."""
        return self.request("GET", "/agents", query={"limit": limit, "offset": offset, "created_via": created_via})

    def create_agent(self, body: dict) -> Any:
        """Create a voice agent — POST /agents (scope: agents:write)."""
        return self.request("POST", "/agents", body=body)

    def get_agent(self, id: str) -> Any:
        """Get a voice agent — GET /agents/{id} (scope: agents:read)."""
        return self.request("GET", f"/agents/{urllib.parse.quote(str(id), safe="")}")

    def update_agent(self, id: str, body: dict) -> Any:
        """Update a voice agent — PATCH /agents/{id} (scope: agents:write)."""
        return self.request("PATCH", f"/agents/{urllib.parse.quote(str(id), safe="")}", body=body)

    def delete_agent(self, id: str) -> Any:
        """Delete a voice agent — DELETE /agents/{id} (scope: agents:write)."""
        return self.request("DELETE", f"/agents/{urllib.parse.quote(str(id), safe="")}")

    def assign_agent_phone_number(self, id: str, body: dict) -> Any:
        """Assign an inbound number — POST /agents/{id}/phone-number (scope: agents:write)."""
        return self.request("POST", f"/agents/{urllib.parse.quote(str(id), safe="")}/phone-number", body=body)

    def unassign_agent_phone_number(self, id: str) -> Any:
        """Unassign the inbound number — DELETE /agents/{id}/phone-number (scope: agents:write)."""
        return self.request("DELETE", f"/agents/{urllib.parse.quote(str(id), safe="")}/phone-number")

    def list_agent_calls(self, id: str, limit: Optional[int] = None, offset: Optional[int] = None) -> Any:
        """List an agent's calls — GET /agents/{id}/calls (scope: calls:read)."""
        return self.request("GET", f"/agents/{urllib.parse.quote(str(id), safe="")}/calls", query={"limit": limit, "offset": offset})

    def create_agent_call(self, id: str, body: dict) -> Any:
        """Start a call with an agent — POST /agents/{id}/calls (scope: calls:write)."""
        return self.request("POST", f"/agents/{urllib.parse.quote(str(id), safe="")}/calls", body=body)

    def list_voices(self) -> Any:
        """List voices — GET /voices (scope: voices:read)."""
        return self.request("GET", "/voices")

    def preview_voice(self, id: str) -> Any:
        """Preview a voice — GET /voices/{id}/preview (scope: voices:read)."""
        return self.request("GET", f"/voices/{urllib.parse.quote(str(id), safe="")}/preview", kind="bytes")

    def list_appointments(self, limit: Optional[int] = None, offset: Optional[int] = None, status: Optional[str] = None, lead_id: Optional[str] = None) -> Any:
        """List appointments — GET /appointments (scope: appointments:read)."""
        return self.request("GET", "/appointments", query={"limit": limit, "offset": offset, "status": status, "lead_id": lead_id})

    def cancel_appointment(self, id: str, body: dict) -> Any:
        """Cancel an appointment — POST /appointments/{id}/cancel (scope: appointments:write)."""
        return self.request("POST", f"/appointments/{urllib.parse.quote(str(id), safe="")}/cancel", body=body)

    def list_deals(self, limit: Optional[int] = None, offset: Optional[int] = None) -> Any:
        """List deals — GET /deals (scope: deals:read). NOT YET AVAILABLE on production."""
        return self.request("GET", "/deals", query={"limit": limit, "offset": offset})

    def update_deal(self, id: str, body: dict) -> Any:
        """Update a deal — PATCH /deals/{id} (scope: deals:write). NOT YET AVAILABLE on production."""
        return self.request("PATCH", f"/deals/{urllib.parse.quote(str(id), safe="")}", body=body)

    def list_webhooks(self) -> Any:
        """List webhook subscriptions — GET /webhooks (scope: webhooks:manage)."""
        return self.request("GET", "/webhooks")

    def create_webhook(self, body: dict) -> Any:
        """Create a webhook subscription — POST /webhooks (scope: webhooks:manage)."""
        return self.request("POST", "/webhooks", body=body)

    def update_webhook(self, id: str, body: dict) -> Any:
        """Update a webhook subscription — PATCH /webhooks/{id} (scope: webhooks:manage)."""
        return self.request("PATCH", f"/webhooks/{urllib.parse.quote(str(id), safe="")}", body=body)

    def delete_webhook(self, id: str) -> Any:
        """Delete a webhook subscription — DELETE /webhooks/{id} (scope: webhooks:manage)."""
        return self.request("DELETE", f"/webhooks/{urllib.parse.quote(str(id), safe="")}")

    def get_open_api_spec(self) -> Any:
        """OpenAPI spec (YAML) — GET /openapi.yaml (scope: none)."""
        return self.request("GET", "/openapi.yaml", kind="text", auth=False)

    def get_getting_started(self) -> Any:
        """Getting-started guide (Markdown) — GET /getting-started.md (scope: none)."""
        return self.request("GET", "/getting-started.md", kind="text", auth=False)

    def get_voice_sdk(self) -> Any:
        """Browser voice SDK (ES module) — GET /sdk/voice.js (scope: none)."""
        return self.request("GET", "/sdk/voice.js", kind="text", auth=False)

    def get_redoc_page(self) -> Any:
        """Built-in reference page — GET /docs (scope: none)."""
        return self.request("GET", "/docs", kind="text", auth=False)
