"""
Bibliothèque officielle Wariguai pour Python (3.8 ou plus récent). Aucune dépendance.
Documentation : https://wariguai.mikpilot.com/docs#sdk

    import os, wariguai
    client = wariguai.Client(os.environ["WARIGUAI_SECRET_KEY"])

    paiement = client.create_payment(amount=5000, reference="CMD-1001", return_url="https://monsite.bf/merci")
    # → rediriger le client vers paiement["checkout_url"]

    evenement = wariguai.verify_webhook(request.get_data(), request.headers.get("Wariguai-Signature", ""), os.environ["WARIGUAI_WEBHOOK_SECRET"])
    if evenement["type"] == "payment.succeeded":
        ...  # commande payée
"""
import hashlib
import hmac
import json
import re
import time
import urllib.error
import urllib.parse
import urllib.request
import uuid

__version__ = "1.0.0"
DEFAULT_BASE_URL = "https://wariguai.mikpilot.com/api/v1"
__all__ = ["Client", "WariguaiError", "verify_webhook"]


class WariguaiError(Exception):
    """Erreur renvoyée par Wariguai (status = code HTTP, code = ex. invalid_amount)."""

    def __init__(self, message, status=0, code="network_error", raw=None):
        super().__init__(message)
        self.status = status
        self.code = code
        self.raw = raw


def verify_webhook(payload, signature_header, secret, tolerance_seconds=300):
    """Vérifie un webhook et renvoie l'événement (dict). Lève WariguaiError si la signature est fausse.

    payload : corps BRUT (bytes ou str), par exemple request.get_data() avec Flask
              ou request.body avec Django.
    """
    if not secret:
        raise WariguaiError("Secret du webhook manquant (whsec_…)", code="missing_secret")
    raw = payload.decode("utf-8") if isinstance(payload, (bytes, bytearray)) else (payload if isinstance(payload, str) else json.dumps(payload, separators=(",", ":"), ensure_ascii=False))
    parts = {}
    for item in str(signature_header or "").split(","):
        if "=" in item:
            key, value = item.split("=", 1)
            parts[key.strip()] = value.strip()
    try:
        timestamp = int(parts.get("t", "0"))
    except ValueError:
        timestamp = 0
    if not timestamp or not parts.get("v1"):
        raise WariguaiError("En-tête Wariguai-Signature absent ou mal formé", 400, "invalid_signature")
    if abs(time.time() - timestamp) > tolerance_seconds:
        raise WariguaiError("Webhook trop ancien (horodatage dépassé)", 400, "timestamp_too_old")
    expected = hmac.new(secret.encode("utf-8"), f"{timestamp}.{raw}".encode("utf-8"), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, parts["v1"]):
        raise WariguaiError("Signature du webhook invalide", 400, "invalid_signature")
    return json.loads(raw)


class Client:
    """Client de l'API Wariguai. La clé secrète reste sur votre serveur."""

    def __init__(self, secret_key, base_url=DEFAULT_BASE_URL, timeout=20):
        if not re.match(r"^sk_(test|live)_[0-9A-Za-z]{32}$", str(secret_key or "")):
            raise ValueError("Clé secrète Wariguai manquante ou invalide : utilise sk_test_… ou sk_live_…")
        self.secret_key = secret_key
        self.mode = "live" if secret_key.startswith("sk_live_") else "test"
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout

    # ───── Paiements ─────

    def create_payment(self, idempotency_key=None, **params):
        """Crée un paiement. Renvoie le paiement (checkout_url, status…)."""
        return self.request("POST", "/payments", params, idempotency_key)

    def get_payment(self, payment_id):
        return self.request("GET", "/payments/" + urllib.parse.quote(str(payment_id), safe=""))

    def list_payments(self, **query):
        query = {k: v for k, v in query.items() if v is not None}
        return self.request("GET", "/payments" + ("?" + urllib.parse.urlencode(query) if query else ""))

    def cancel_payment(self, payment_id):
        return self.request("POST", "/payments/" + urllib.parse.quote(str(payment_id), safe="") + "/cancel")

    def simulate_payment(self, payment_id, outcome="succeeded"):
        """Mode test seulement : 'succeeded' ou 'failed'."""
        return self.request("POST", "/payments/" + urllib.parse.quote(str(payment_id), safe="") + "/simulate", {"outcome": outcome})

    def is_paid(self, payment_id):
        """True si le paiement est réussi (à appeler sur votre page de retour)."""
        return self.get_payment(payment_id).get("status") == "succeeded"

    # ───── Retraits, solde, moyens de paiement ─────

    def create_payout(self, idempotency_key=None, **params):
        return self.request("POST", "/payouts", params, idempotency_key)

    def get_payout(self, payout_id):
        return self.request("GET", "/payouts/" + urllib.parse.quote(str(payout_id), safe=""))

    def get_balance(self):
        return self.request("GET", "/balance")

    def get_channels(self):
        return self.request("GET", "/channels")

    # ───── Requête HTTP ─────

    def request(self, method, path, body=None, idempotency_key=None):
        # Une même clé d'idempotence pour l'essai et le nouvel essai : jamais deux paiements.
        key = (idempotency_key or uuid.uuid4().hex) if method == "POST" else None
        last_error = None
        for attempt in (1, 2):
            headers = {
                "Authorization": "Bearer " + self.secret_key,
                "Accept": "application/json",
                "User-Agent": "wariguai-python/" + __version__,
            }
            data = None
            if method == "POST":
                headers["Content-Type"] = "application/json"
                headers["Idempotency-Key"] = key
                data = json.dumps(body or {}, ensure_ascii=False).encode("utf-8")
            req = urllib.request.Request(self.base_url + path, data=data, headers=headers, method=method)
            try:
                with urllib.request.urlopen(req, timeout=self.timeout) as res:
                    text = res.read().decode("utf-8")
                    return json.loads(text) if text else {}
            except urllib.error.HTTPError as error:
                text = error.read().decode("utf-8", "replace")
                try:
                    payload = json.loads(text)
                except ValueError:
                    payload = None
                err = payload.get("error", {}) if isinstance(payload, dict) and isinstance(payload.get("error"), dict) else {}
                last_error = WariguaiError(err.get("message") or "Erreur Wariguai %d" % error.code, error.code, err.get("code") or "api_error", payload)
                if error.code < 500 or attempt == 2:
                    raise last_error
            except (urllib.error.URLError, TimeoutError) as error:
                last_error = WariguaiError("Wariguai injoignable : %s" % error)
            if attempt == 1:
                time.sleep(0.8)
        raise last_error
