"""Kita Risk server-side client. One file, standard library only.

Same shape as a Plaid Link integration: your server creates a session with its
secret API key, hands the browser or app a short-lived client token, and the
Kita widget does the rest. The API key never leaves your server.

    from kita_risk import KitaRisk

    kita = KitaRisk(api_key=os.environ["KITA_API_KEY"], base_url=os.environ["KITA_BASE_URL"])
    session = kita.create_session(reference_id=loan_id, origin="https://app.example.com")
    # -> give session["session_id"] and session["client_token"] to your page
"""
import hashlib
import hmac
import json
import time
import urllib.error
import urllib.request

__version__ = "0.4.0"


class KitaRiskError(Exception):
    """An API error. `code` is Kita's stable error code, e.g. origin_not_allowed."""
    def __init__(self, status, code, request_id=None):
        super().__init__(f"{status} {code}")
        self.status, self.code, self.request_id = status, code, request_id


class KitaRisk:
    def __init__(self, api_key, base_url, timeout=20, opener=urllib.request.urlopen):
        if not base_url.startswith("https://") and not base_url.startswith("http://localhost"):
            raise ValueError("base_url must be HTTPS")
        self.api_key, self.base_url, self.timeout, self.opener = api_key, base_url.rstrip("/"), timeout, opener

    def _call(self, method, path, body=None, headers=None):
        request = urllib.request.Request(
            self.base_url + "/v1/risk/sessions" + path, method=method,
            data=None if body is None else json.dumps(body).encode(),
            headers={"X-API-Key": self.api_key, "Content-Type": "application/json", **(headers or {})})
        try:
            with self.opener(request, timeout=self.timeout) as response:
                raw = response.read()
        except urllib.error.HTTPError as exc:
            error = (json.loads(exc.read() or b"{}").get("error") or {})
            raise KitaRiskError(exc.code, error.get("code", "http_error"), error.get("request_id")) from None
        return json.loads(raw) if raw else None

    def create_session(self, reference_id, origin, display_name="Your lender", country="MX"):
        """Start (or resume) the upload session for one applicant.

        `reference_id` is your applicant or loan ID: it is stored with everything
        Kita archives, so it is how results are joined to outcomes later. Calling
        again with the same ID returns the same session with a fresh client token.
        `origin` is the exact web origin that will embed the widget.
        """
        return self._call("POST", "", {"reference_id": reference_id, "origin": origin, "display_name": display_name,
                                       "country": country, "location": "disabled"},
                          {"Idempotency-Key": "session:" + reference_id})

    def client_token(self, session_id):
        """A fresh client token for a session the applicant is returning to."""
        return self._call("POST", f"/{session_id}/client-token")

    def status(self, session_id):
        """collecting, queued, processing, completed, partial or failed, plus per-file status and archive_status."""
        return self._call("GET", f"/{session_id}")

    def delete(self, session_id):
        """Delete the session, its files and its archived results."""
        self._call("DELETE", f"/{session_id}")

    @staticmethod
    def verify_webhook(secret, signature_header, raw_body, tolerance=300):
        """True when a `Kita-Signature: t=<unix>,v1=<hex>` header is authentic and recent."""
        parts = dict(item.split("=", 1) for item in signature_header.split(",") if "=" in item)
        if not parts.get("t", "").isdigit() or abs(time.time() - int(parts["t"])) > tolerance:
            return False
        expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
        return hmac.compare_digest(expected, parts.get("v1", ""))
