"""Sealine — PyNaCl sealed-box evidence encryption + recipient key management."""

from __future__ import annotations

import base64
import hashlib
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional
from uuid import uuid4

from nacl.public import PrivateKey, PublicKey, SealedBox

from psyc import DATA_DIR, log
from psyc.models import SealedPackage
from psyc.result import Err, Ok, Result


KEYS_DIR = DATA_DIR / "keys"
SEALED_DIR = DATA_DIR / "sealed"

_log = log.get(__name__)


def generate_recipient_keys(recipient: str) -> Path:
    KEYS_DIR.mkdir(parents=True, exist_ok=True)
    sk = PrivateKey.generate()
    pk = sk.public_key
    pub_path = KEYS_DIR / f"{recipient}.pub"
    sec_path = KEYS_DIR / f"{recipient}.sec"
    pub_path.write_text(base64.b64encode(bytes(pk)).decode())
    sec_path.write_text(base64.b64encode(bytes(sk)).decode())
    sec_path.chmod(0o600)
    _log.info("seal.keys.generated", recipient=recipient, pub_path=str(pub_path))
    return pub_path


def load_public_key(recipient: str) -> Result[PublicKey, str]:
    pub_path = KEYS_DIR / f"{recipient}.pub"
    if not pub_path.exists():
        return Err(f"no public key for recipient: {recipient}")
    return Ok(PublicKey(base64.b64decode(pub_path.read_text())))


def load_private_key(recipient: str) -> Result[PrivateKey, str]:
    sec_path = KEYS_DIR / f"{recipient}.sec"
    if not sec_path.exists():
        return Err(f"no private key for recipient: {recipient}")
    return Ok(PrivateKey(base64.b64decode(sec_path.read_text())))


def list_recipients() -> List[str]:
    if not KEYS_DIR.exists():
        return []
    return sorted(p.stem for p in KEYS_DIR.glob("*.pub"))


def seal(plaintext: bytes, recipients: List[str], metadata: Optional[Dict[str, str]] = None) -> Result[SealedPackage, str]:
    if not recipients:
        return Err("no recipients specified")
    SEALED_DIR.mkdir(parents=True, exist_ok=True)
    plaintext_hash = hashlib.sha256(plaintext).hexdigest()
    ciphertext_per_recipient: Dict[str, str] = {}
    for name in recipients:
        pk_result = load_public_key(name)
        if isinstance(pk_result, Err):
            return Err(f"recipient {name}: {pk_result.reason}")
        box = SealedBox(pk_result.value)
        ciphertext = box.encrypt(plaintext)
        ciphertext_per_recipient[name] = base64.b64encode(ciphertext).decode()
    package = SealedPackage(
        package_id=f"PKG-{uuid4().hex[:12].upper()}",
        created_at=datetime.now(timezone.utc),
        plaintext_hash=plaintext_hash,
        recipients=list(recipients),
        ciphertext_per_recipient=ciphertext_per_recipient,
        metadata=metadata or {},
    )
    pkg_path = SEALED_DIR / f"{package.package_id}.json"
    pkg_path.write_text(package.model_dump_json(indent=2))
    _log.info("seal.packaged", package_id=package.package_id, recipients=recipients, plaintext_hash=plaintext_hash[:16])
    return Ok(package)


def load_package(package_id: str) -> Result[SealedPackage, str]:
    pkg_path = SEALED_DIR / f"{package_id}.json"
    if not pkg_path.exists():
        return Err(f"no sealed package: {package_id}")
    return Ok(SealedPackage.model_validate_json(pkg_path.read_text()))


def list_packages() -> List[str]:
    if not SEALED_DIR.exists():
        return []
    return sorted(p.stem for p in SEALED_DIR.glob("PKG-*.json"))


def unseal(package_id: str, recipient: str) -> Result[bytes, str]:
    pkg_result = load_package(package_id)
    if isinstance(pkg_result, Err):
        return pkg_result
    pkg = pkg_result.value
    if recipient not in pkg.ciphertext_per_recipient:
        return Err(f"recipient {recipient} is not on this package")
    sk_result = load_private_key(recipient)
    if isinstance(sk_result, Err):
        return sk_result
    box = SealedBox(sk_result.value)
    try:
        plaintext = box.decrypt(base64.b64decode(pkg.ciphertext_per_recipient[recipient]))
    except Exception as exc:
        return Err(f"decrypt failed: {exc}")
    return Ok(plaintext)
