"""Connexis Python SDK — v1.0.0
MIT License — (c) Connexis Group

Tiny, single-file client for the Connexis Lead Ingestion API.
Requires:  pip install requests

Usage:
    from connexis_sdk import Connexis, ConnexisError

    client = Connexis(api_key="pk_live_...")

    res = client.ingest({
        "sector":     "mortgages",
        "first_name": "Olivia",
        "last_name":  "Hughes",
        "email":      "olivia.hughes@example.com",
        "phone":      "+447900654321",
        "country":    "GB",
        "consent_token": "trustedform_or_jornaya_token_here",
        "extra_fields": {"age": 36, "loan_amount": 185000},
    })
    print(res)
    # -> {'accepted': True, 'lead_id': '...', 'price_gbp': 38.0,
    #     'publisher_earning_gbp': 34.2, 'buyer_matched': True}

Retries: exponential backoff on 5xx / network errors. 4xx surface immediately.
"""

from __future__ import annotations

import time
from typing import Any, Optional

import requests

__version__ = "1.0.0"

DEFAULT_BASE = "https://api.connex.is"
DEFAULT_TIMEOUT = 10.0     # seconds
DEFAULT_RETRIES = 2


class ConnexisError(Exception):
    """Raised on any API or network error."""

    def __init__(self, message: str, status: Optional[int] = None,
                 code: Optional[str] = None, body: Any = None):
        super().__init__(message)
        self.status = status
        self.code = code
        self.body = body


class Connexis:
    """Minimal Connexis Lead Ingestion client.

    Example:
        client = Connexis(api_key="pk_live_...")
        res    = client.ingest({...})
    """

    REQUIRED_FIELDS = ("sector", "first_name", "last_name", "email", "phone")

    def __init__(self, api_key: str,
                 base_url: str = DEFAULT_BASE,
                 timeout: float = DEFAULT_TIMEOUT,
                 retries: int = DEFAULT_RETRIES):
        if not api_key:
            raise ConnexisError("api_key required (your pk_live_... or pk_test_... key)")
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self.retries = retries
        self._session = requests.Session()
        self._session.headers.update({
            "Content-Type": "application/json",
            "X-API-Key":    api_key,
            "User-Agent":   f"connexis-python/{__version__}",
        })

    def ingest(self, lead: dict) -> dict:
        """Submit a lead for routing. Returns the routing decision."""
        for f in self.REQUIRED_FIELDS:
            if not lead.get(f):
                raise ConnexisError(f"Missing required field: {f}")
        return self._request("POST", "/api/v1/leads/ingest", lead)

    def postback(self, lead_id: str, payload: dict) -> dict:
        """Fire a disposition postback (sold / junk / unreachable) for a delivered lead."""
        if not lead_id:
            raise ConnexisError("lead_id required")
        if not payload.get("postback_token"):
            raise ConnexisError("postback_token required")
        return self._request("POST", f"/api/v1/leads/{lead_id}/postback", payload)

    def _request(self, method: str, path: str, body: Optional[dict] = None,
                 attempt: int = 0) -> dict:
        url = f"{self.base_url}{path}"
        try:
            r = self._session.request(method, url, json=body, timeout=self.timeout)
        except requests.RequestException as e:
            if attempt < self.retries:
                time.sleep(0.2 * (2 ** attempt))
                return self._request(method, path, body, attempt + 1)
            raise ConnexisError(f"Network error: {e}", code="NETWORK") from e

        try:
            parsed = r.json()
        except ValueError:
            parsed = None

        if r.status_code >= 500 and attempt < self.retries:
            time.sleep(0.2 * (2 ** attempt))
            return self._request(method, path, body, attempt + 1)

        if not r.ok:
            msg = (parsed or {}).get("detail") or (parsed or {}).get("message") \
                  or f"Connexis API {r.status_code}"
            raise ConnexisError(msg, status=r.status_code, body=parsed)

        return parsed or {}


__all__ = ["Connexis", "ConnexisError", "__version__"]
