"""
License Signing Abstraction

Single place to swap signing algorithm. Currently: Ed25519 (classical elliptic curve,
via the Python `cryptography` package).

NOTE: This module uses Ed25519 exclusively for license file signing and verification.
No post-quantum cryptographic algorithm is implemented here or elsewhere in this product.
A future migration to a post-quantum signature scheme (e.g., ML-DSA / NIST FIPS 204)
is documented in ADR-001 (docs/adr/ADR-001-license-signing.md) but has not been
implemented and no PQC library is a dependency of this project.

To migrate signing algorithm when ready:
1. Generate new keypair using the target algorithm
2. Replace sign() and verify() implementations below
3. Update ALGORITHM constant
4. Re-generate all offline license files with the new key
"""

import base64
import json
import logging

logger = logging.getLogger(__name__)

ALGORITHM = "Ed25519"  # Algorithm used for all license files


def canonical_json(data: dict) -> bytes:
    """Canonical JSON encoding — must be identical on sign and verify sides."""
    return json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8")


def sign(license_data: dict, private_key_b64: str) -> str:
    """Sign license_data dict. Returns base64-encoded signature string."""
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
    raw_key = base64.b64decode(private_key_b64)
    private_key = Ed25519PrivateKey.from_private_bytes(raw_key)
    sig_bytes = private_key.sign(canonical_json(license_data))
    return base64.b64encode(sig_bytes).decode()


def verify(license_data: dict, signature_b64: str, public_key_b64: str, algorithm: str = "Ed25519") -> bool:
    """
    Verify signature over license_data dict. Returns True if valid.
    Currently only Ed25519 is supported.
    """
    try:
        msg = canonical_json(license_data)
        sig_bytes = base64.b64decode(signature_b64)
        raw_key = base64.b64decode(public_key_b64)

        if algorithm == "Ed25519":
            from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
            Ed25519PublicKey.from_public_bytes(raw_key).verify(sig_bytes, msg)
            return True

        logger.warning(f"Unknown signature algorithm: {algorithm}")
        return False
    except Exception as e:
        logger.warning(f"Signature verification failed ({algorithm}): {e}")
        return False


def make_license_file(license_data: dict, private_key_b64: str, include_public_key: str = "") -> dict:
    """Wrap license_data in the signed offline file envelope."""
    envelope = {
        "license_data": license_data,
        "signature": sign(license_data, private_key_b64),
        "algorithm": ALGORITHM,
    }
    if include_public_key:
        envelope["public_key"] = include_public_key
    return envelope
