"""Criptografia local del xamiSDK (lado cliente del canal seguro).

Espejo de minihsm/sdk_channel.py del servidor, pero desde la perspectiva del SDK:
- El SDK tiene su propio par RSA (privada NUNCA sale del SDK).
- Para EMITIR hacia Xami: cifra con la publica de Xami (JWE) y firma con su privada (JWS).
- Para RECIBIR de Xami: verifica con la publica de Xami (JWS) y descifra con su privada (JWE).

Sobre criptografico (identico al servidor): JWE alg=RSA-OAEP-256 enc=A256GCM, JWS alg=PS256.
Ver docs/arquitectura_xamiSDK.md.
"""
from __future__ import annotations
import json
import time

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from jwcrypto import jwk, jwe, jws
from jwcrypto.common import json_encode


def generate_keypair() -> tuple[str, str]:
    """Genera un par RSA-3072. Devuelve (private_pem, public_pem). La privada se
    guarda localmente y nunca sale del SDK."""
    key = rsa.generate_private_key(public_exponent=65537, key_size=3072)
    priv_pem = key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()).decode()
    pub_pem = key.public_key().public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo).decode()
    return priv_pem, pub_pem


def _jwk_from_pem(pem: str) -> jwk.JWK:
    return jwk.JWK.from_pem(pem.encode() if isinstance(pem, str) else pem)


def seal_for_xami(payload: dict, xami_pubkey_pem: str, sdk_privkey_pem: str) -> str:
    """Empaqueta un payload para Xami: JWE (cifrado a la publica de Xami) + JWS
    (firmado con la privada del SDK). Devuelve el JWS compacto."""
    xami_pub = _jwk_from_pem(xami_pubkey_pem)
    sdk_priv = _jwk_from_pem(sdk_privkey_pem)

    plaintext = json.dumps(payload, separators=(",", ":")).encode()
    token = jwe.JWE(plaintext, recipient=xami_pub,
                    protected=json_encode({"alg": "RSA-OAEP-256", "enc": "A256GCM"}))
    jwe_compact = token.serialize(compact=True)

    sig = jws.JWS(jwe_compact.encode())
    sig.add_signature(sdk_priv, alg="PS256",
                      protected=json_encode({"alg": "PS256", "typ": "xami-sdk"}))
    return sig.serialize(compact=True)


def open_from_xami(envelope: str, xami_pubkey_pem: str, sdk_privkey_pem: str) -> dict:
    """Abre un sobre recibido de Xami: verifica la firma (publica de Xami) y descifra
    (privada del SDK). Devuelve el payload dict. Lanza si algo falla."""
    xami_pub = _jwk_from_pem(xami_pubkey_pem)
    sdk_priv = _jwk_from_pem(sdk_privkey_pem)

    verifier = jws.JWS()
    verifier.deserialize(envelope)
    verifier.verify(xami_pub)              # lanza si la firma de Xami no valida
    jwe_compact = verifier.payload.decode()

    token = jwe.JWE()
    token.deserialize(jwe_compact)
    token.decrypt(sdk_priv)                # lanza si no puede descifrar
    return json.loads(token.payload.decode())


def now_ts() -> int:
    return int(time.time())
