"""ContentDividend URVP Open Verification SDK v1.0.0.

Standard-library HTTP client. Offline Ed25519 verification uses the optional
`cryptography` package if installed. Live status verification requires no
third-party dependency.
"""
from __future__ import annotations

import base64
import hashlib
import json
from urllib.request import Request, urlopen
from urllib.error import HTTPError


class URVPError(RuntimeError):
    pass


def _b64u_decode(value: str) -> bytes:
    value = value.strip()
    value += "=" * (-len(value) % 4)
    return base64.urlsafe_b64decode(value.encode("ascii"))


def _canonical_json(value) -> str:
    return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)


class URVPClient:
    def __init__(self, base_url="https://app.contentdividend.com", bearer_token=None, timeout=20.0):
        self.base_url = base_url.rstrip("/")
        self.bearer_token = bearer_token
        self.timeout = timeout

    def _json(self, method, path, body=None, token=None):
        data = None if body is None else json.dumps(body).encode("utf-8")
        headers = {"Accept":"application/json"}
        if data is not None:
            headers["Content-Type"]="application/json"
        auth = token if token is not None else self.bearer_token
        if auth:
            headers["Authorization"]="Bearer "+auth
        req=Request(self.base_url+path,data=data,method=method,headers=headers)
        try:
            with urlopen(req,timeout=self.timeout) as response:
                raw=response.read().decode("utf-8")
                return json.loads(raw) if raw else {}
        except HTTPError as exc:
            try: detail=json.loads(exc.read().decode("utf-8"))
            except Exception: detail={"detail":str(exc)}
            raise URVPError(detail.get("detail") or str(exc)) from exc

    def verify_rights(self, resource, requested_use, bearer_token=None):
        return self._json("POST","/v1/rights/verify",
                          {"resource":resource,"requested_use":requested_use},
                          token=bearer_token)

    def issue_receipt(self, resource, requested_use, bearer_token=None):
        return self._json("POST","/v1/rights/verify-receipt",
                          {"resource":resource,"requested_use":requested_use},
                          token=bearer_token)

    def verify_receipt_remote(self, receipt):
        return self._json("POST","/v1/public/rights-receipts/verify",{"receipt":receipt},token="")

    def receipt_status(self, receipt_id):
        return self._json("GET","/v1/public/rights-receipts/"+receipt_id+"/status",token="")

    def signing_keys(self):
        return self._json("GET","/v1/public/license-credentials/keys",token="")

    @staticmethod
    def verify_receipt_offline(receipt, public_key_b64u):
        """Verify receipt integrity/signature locally.

        Requires: pip install cryptography

        This verifies provenance/integrity only. Call receipt_status() to determine
        whether the underlying rights are still current.
        """
        try:
            from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
        except Exception as exc:
            raise URVPError("Offline verification requires the optional cryptography package.") from exc

        if not isinstance(receipt, dict) or not isinstance(receipt.get("proof"), dict):
            raise URVPError("A signed receipt object is required.")
        proof=receipt["proof"]
        unsigned={k:v for k,v in receipt.items() if k!="proof"}
        canonical=_canonical_json(unsigned).encode("utf-8")
        calculated=hashlib.sha256(canonical).hexdigest()
        expected=str(proof.get("signed_payload_sha256") or "")
        signature=str(proof.get("signature_b64u") or "")
        valid_hash=bool(expected and expected==calculated)
        try:
            public=Ed25519PublicKey.from_public_bytes(_b64u_decode(public_key_b64u))
            public.verify(_b64u_decode(signature),canonical)
            valid_signature=True
        except Exception:
            valid_signature=False
        return {
            "signature_valid": bool(valid_signature and valid_hash),
            "proof_hash_matches": valid_hash,
            "signed_payload_sha256": calculated,
            "offline_signature_does_not_prove_current_rights": True,
        }
