from __future__ import annotations

import base64
import hashlib
import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Callable

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.hazmat.primitives.serialization import load_pem_public_key


BASE = Path(__file__).resolve().parent
RECEIPT_PATH = BASE / ".deploy" / "activation-receipt.json"
MACHINE_ID_PATH = Path("/etc/machine-id")
PRODUCT = "levbot_green"
KID = "prod-2026-08-04-01"
PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA/eQtv2E5jl1T10OgN4FaPgOg3ez0bTgqv1P24D5EsbY=
-----END PUBLIC KEY-----
"""


class LicenseReceiptError(RuntimeError):
    pass


def normalize_machine_id(raw: str) -> str:
    value = str(raw or "").strip().lower()
    if not value:
        raise LicenseReceiptError("machine-id is empty")
    if not re.fullmatch(r"[0-9a-f]{32}", value):
        raise LicenseReceiptError("machine-id is malformed")
    return value


def server_fingerprint_hash(raw_machine_id: str) -> str:
    normalized = normalize_machine_id(raw_machine_id)
    return hashlib.sha256(normalized.encode("utf-8")).hexdigest()


def canonical_json(payload: dict[str, Any]) -> bytes:
    return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")


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


def _public_key(public_key_pem: str = PUBLIC_KEY_PEM) -> Ed25519PublicKey:
    key = load_pem_public_key(public_key_pem.encode("ascii"))
    if not isinstance(key, Ed25519PublicKey):
        raise LicenseReceiptError("receipt public key is invalid")
    return key


def verify_signature(payload: dict[str, Any], signature: str, public_key_pem: str = PUBLIC_KEY_PEM) -> bool:
    try:
        _public_key(public_key_pem).verify(b64url_decode(signature), canonical_json(payload))
    except (InvalidSignature, ValueError):
        return False
    return True


def parse_receipt_datetime(value: str) -> datetime:
    parsed = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)
    return parsed.astimezone(timezone.utc)


def verify_expiration(payload: dict[str, Any], *, now: datetime | None = None) -> None:
    if "expires_at" not in payload:
        raise LicenseReceiptError("activation receipt expires_at is missing")
    expires_at = payload.get("expires_at")
    if expires_at is None:
        return
    if not isinstance(expires_at, str) or not expires_at.strip():
        raise LicenseReceiptError("activation receipt expires_at is invalid")
    try:
        parsed = parse_receipt_datetime(expires_at)
    except ValueError as exc:
        raise LicenseReceiptError("activation receipt expires_at is invalid") from exc
    if (now or datetime.now(timezone.utc)).astimezone(timezone.utc) >= parsed:
        raise LicenseReceiptError("activation receipt is expired")


def verify_local_receipt(
    *,
    receipt_path: Path = RECEIPT_PATH,
    machine_id_path: Path = MACHINE_ID_PATH,
    public_key_pem: str = PUBLIC_KEY_PEM,
) -> dict[str, Any]:
    try:
        receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
    except FileNotFoundError as exc:
        raise LicenseReceiptError("activation receipt is missing") from exc
    except json.JSONDecodeError as exc:
        raise LicenseReceiptError("activation receipt is malformed") from exc
    if not isinstance(receipt, dict):
        raise LicenseReceiptError("activation receipt is malformed")
    payload = receipt.get("payload")
    signature = receipt.get("signature")
    if not isinstance(payload, dict) or not isinstance(signature, str) or not signature:
        raise LicenseReceiptError("activation receipt is malformed")
    if not verify_signature(payload, signature, public_key_pem):
        raise LicenseReceiptError("activation receipt signature is invalid")
    if payload.get("schema_version") != 1:
        raise LicenseReceiptError("activation receipt schema is unsupported")
    if str(payload.get("kid") or "") != KID:
        raise LicenseReceiptError("activation receipt kid is unexpected")
    if str(payload.get("product") or "").strip().lower() != PRODUCT:
        raise LicenseReceiptError("activation receipt product is unexpected")
    try:
        fingerprint = server_fingerprint_hash(machine_id_path.read_text(encoding="utf-8"))
    except FileNotFoundError as exc:
        raise LicenseReceiptError("machine-id is missing") from exc
    if str(payload.get("server_fingerprint_hash") or "") != fingerprint:
        raise LicenseReceiptError("activation receipt server fingerprint mismatch")
    verify_expiration(payload)
    return receipt


def assert_local_receipt_or_die(log: Callable[[str], None] = print) -> None:
    try:
        verify_local_receipt()
    except LicenseReceiptError as exc:
        log(f"LICENSE RECEIPT: FAIL: {exc}")
        raise
    log("LICENSE RECEIPT: OK")
