import base64
import httpx
from .exceptions import AuthenticationError, ProofError, SDKError, VerificationError
from .utils import logger


class Proof:
    """A cryptographic proof of AI content origin."""

    def __init__(self, data: dict):
        self.id = data["id"]
        self.fingerprint = data["fingerprint"]
        self.provider = data["provider"]
        self.model = data["model"]
        self.content_type = data["content_type"]
        self.timestamp = data["timestamp"]
        self.signature = data["signature"]
        self.verify_url = data["verify_url"]
        self.metadata = data.get("metadata")

    def __repr__(self):
        return (
            f"Proof(id={self.id!r}, provider={self.provider!r}, model={self.model!r})"
        )


class VerificationResult:
    """Result of verifying content against the 0byte registry."""

    def __init__(self, data: dict):
        self.matched = data["matched"]
        self.confidence = data["confidence"]
        self.proof = Proof(data["proof"]) if data.get("proof") else None

    def __repr__(self):
        if self.matched:
            return f"VerificationResult(matched=True, confidence={self.confidence:.3f}, proof={self.proof.id!r})"
        return "VerificationResult(matched=False)"


class Client:
    """0byte API client for stamping and verifying AI content."""

    def __init__(
        self,
        api_key: str = None,
        endpoint: str = "https://api.0byte.tech",
        timeout: float = 30.0,
    ):
        self.api_key = api_key
        self.endpoint = endpoint.rstrip("/")
        self._http = httpx.Client(timeout=timeout)

    def stamp(
        self,
        content: bytes,
        content_type: str,
        provider: str,
        model: str,
        metadata: dict = None,
    ) -> Proof:
        """Create a cryptographic proof of origin for AI-generated content."""
        if not self.api_key:
            raise AuthenticationError(
                "API key required for stamping. Pass api_key to Client()."
            )

        payload = {
            "content": base64.b64encode(content).decode(),
            "content_type": content_type,
            "provider": provider,
            "model": model,
        }
        if metadata:
            payload["metadata"] = metadata

        logger.info("Stamping content (provider=%s, model=%s)", provider, model)

        response = self._http.post(
            f"{self.endpoint}/v1/stamp",
            json=payload,
            headers={"Authorization": f"Bearer {self.api_key}"},
        )

        if response.status_code == 401:
            raise AuthenticationError("Invalid API key")
        if response.status_code != 200:
            raise ProofError(f"Stamp failed ({response.status_code}): {response.text}")

        data = response.json()
        logger.info("Proof created: %s", data["id"])
        return Proof(data)

    def verify(
        self, content: bytes, content_type: str = "image/png"
    ) -> VerificationResult:
        """Verify content against the 0byte registry."""
        payload = {
            "content": base64.b64encode(content).decode(),
            "content_type": content_type,
        }

        logger.info("Verifying content against registry")

        response = self._http.post(
            f"{self.endpoint}/v1/verify",
            json=payload,
        )

        if response.status_code != 200:
            raise VerificationError(
                f"Verification failed ({response.status_code}): {response.text}"
            )

        data = response.json()
        result = VerificationResult(data)
        if result.matched:
            logger.info(
                "Match found: %s (confidence=%.3f)", result.proof.id, result.confidence
            )
        else:
            logger.info("No match found in registry")
        return result

    # --- Key Management ---

    def _auth_headers(self) -> dict:
        if not self.api_key:
            raise AuthenticationError("API key required. Pass api_key to Client().")
        return {"Authorization": f"Bearer {self.api_key}"}

    def create_key(self, name: str) -> dict:
        """Create a new API key. Returns dict with id, name, created_at, and key (shown once)."""
        response = self._http.post(
            f"{self.endpoint}/v1/keys",
            json={"name": name},
            headers=self._auth_headers(),
        )

        if response.status_code == 401:
            raise AuthenticationError("Invalid API key")
        if response.status_code not in (200, 201):
            raise SDKError(
                f"Create key failed ({response.status_code}): {response.text}"
            )

        data = response.json()
        logger.info("API key created: id=%s, name=%s", data["id"], data["name"])
        return data

    def list_keys(self) -> list[dict]:
        """List all API keys (raw keys are never returned)."""
        response = self._http.get(
            f"{self.endpoint}/v1/keys",
            headers=self._auth_headers(),
        )

        if response.status_code == 401:
            raise AuthenticationError("Invalid API key")
        if response.status_code != 200:
            raise SDKError(
                f"List keys failed ({response.status_code}): {response.text}"
            )

        return response.json()

    def revoke_key(self, key_id: str) -> bool:
        """Revoke an API key by ID. Returns True if deleted."""
        response = self._http.delete(
            f"{self.endpoint}/v1/keys/{key_id}",
            headers=self._auth_headers(),
        )

        if response.status_code == 401:
            raise AuthenticationError("Invalid API key")
        if response.status_code != 200:
            raise SDKError(
                f"Revoke key failed ({response.status_code}): {response.text}"
            )

        return response.json().get("deleted", False)

    # --- Proof Lookup ---

    def get_proof(self, proof_id: str) -> Proof:
        """Look up a proof by ID."""
        response = self._http.get(f"{self.endpoint}/v1/proofs/{proof_id}")

        if response.status_code == 404:
            raise ProofError(f"Proof not found: {proof_id}")
        if response.status_code != 200:
            raise ProofError(f"Lookup failed ({response.status_code}): {response.text}")

        return Proof(response.json())

    def close(self):
        self._http.close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
