"""Asymmetric attestation signing and verification utilities."""

from __future__ import annotations

import base64
import hashlib
import json
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
    Ed25519PrivateKey,
    Ed25519PublicKey,
)

from .types import DataAttestation, TrustLane


def compute_input_hash(payload: Any) -> str:
    """Compute deterministic SHA-256 hash for payload integrity tracking."""
    canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str)
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()


def _canonical_attestation_payload(
    *,
    dataset_id: str,
    lane: TrustLane,
    masked: bool,
    policy_version: str,
    input_hash: str,
    issued_at: str,
    expires_at: Optional[str],
    public_key_id: str,
) -> bytes:
    payload = {
        "dataset_id": dataset_id,
        "lane": lane.value,
        "masked": bool(masked),
        "policy_version": policy_version,
        "input_hash": input_hash,
        "issued_at": issued_at,
        "expires_at": expires_at or "",
        "public_key_id": public_key_id,
    }
    canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
    return canonical.encode("utf-8")


def create_attestation(
    *,
    dataset_id: str,
    lane: TrustLane,
    masked: bool,
    policy_version: str,
    input_hash: str,
    private_key_pem: str,
    public_key_id: str,
    expires_at: Optional[str] = None,
) -> DataAttestation:
    """Create an Ed25519-signed attestation."""
    issued_at = datetime.now(timezone.utc).isoformat()
    message = _canonical_attestation_payload(
        dataset_id=dataset_id,
        lane=lane,
        masked=masked,
        policy_version=policy_version,
        input_hash=input_hash,
        issued_at=issued_at,
        expires_at=expires_at,
        public_key_id=public_key_id,
    )
    private_key = serialization.load_pem_private_key(
        private_key_pem.encode("utf-8"),
        password=None,
    )
    if not isinstance(private_key, Ed25519PrivateKey):
        raise ValueError("Only Ed25519 private keys are supported")

    signature = private_key.sign(message)
    return DataAttestation(
        dataset_id=dataset_id,
        lane=lane,
        masked=masked,
        policy_version=policy_version,
        input_hash=input_hash,
        signature=base64.b64encode(signature).decode("utf-8"),
        public_key_id=public_key_id,
        issued_at=issued_at,
        expires_at=expires_at,
    )


def verify_attestation(
    attestation: DataAttestation | Dict[str, Any],
    public_key_registry: Dict[str, str],
) -> Dict[str, Any]:
    """Verify attestation signature and temporal validity."""
    if not isinstance(attestation, DataAttestation):
        attestation = DataAttestation(**attestation)

    public_key_pem = public_key_registry.get(attestation.public_key_id)
    if not public_key_pem:
        return {"valid": False, "reason": "unknown_public_key_id"}

    message = _canonical_attestation_payload(
        dataset_id=attestation.dataset_id,
        lane=attestation.lane,
        masked=attestation.masked,
        policy_version=attestation.policy_version,
        input_hash=attestation.input_hash,
        issued_at=attestation.issued_at,
        expires_at=attestation.expires_at,
        public_key_id=attestation.public_key_id,
    )

    public_key = serialization.load_pem_public_key(public_key_pem.encode("utf-8"))
    if not isinstance(public_key, Ed25519PublicKey):
        return {"valid": False, "reason": "unsupported_public_key_type"}

    try:
        signature = base64.b64decode(attestation.signature.encode("utf-8"))
        public_key.verify(signature, message)
    except Exception:
        return {"valid": False, "reason": "invalid_signature"}

    if attestation.expires_at:
        try:
            expiry = datetime.fromisoformat(attestation.expires_at.replace("Z", "+00:00"))
            now = datetime.now(timezone.utc)
            if expiry.tzinfo is None:
                expiry = expiry.replace(tzinfo=timezone.utc)
            if now > expiry:
                return {"valid": False, "reason": "expired"}
        except Exception:
            return {"valid": False, "reason": "invalid_expiry"}

    return {"valid": True, "reason": "ok"}


def persist_attestation_event(
    event: Dict[str, Any],
    *,
    base_dir: str = "data/datashield",
) -> str:
    """Persist an attestation verification event and return file path."""
    root = Path(base_dir) / "attestations"
    root.mkdir(parents=True, exist_ok=True)
    ts = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    event_id = event.get("event_id") or f"evt_{uuid.uuid4().hex[:10]}"
    path = root / f"{ts}_{event_id}.json"
    payload = dict(event)
    payload.setdefault("event_id", event_id)
    payload.setdefault("recorded_at", datetime.now(timezone.utc).isoformat())
    path.write_text(json.dumps(payload, indent=2, default=str), encoding="utf-8")
    return str(path)
