"""Persistent witness identities — gap-to-done #2 (second half).

A qualified witness master must be the SAME identity across restarts: its
signing key lives in a 32-byte seed file (created once, mode 0600), and
the qualified-witness set lives in a small registry file mapping witness
id → Ed25519 public key (hex). The registry is local operator config —
loaded at boot, passed to `witness.confirm` as the qualified dict; the
seam law (no JSON across the seam) is untouched, nothing here crosses a
wire. Rotation / suspension / revocation of qualified witnesses is a
POHAT-authority decision recorded as NEW records — that lifecycle is a
separate owner-gated step; this module only makes identities durable.
"""

from __future__ import annotations

import json
import os
from pathlib import Path

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


def load_or_create_key(path: str | Path) -> Ed25519PrivateKey:
    """The witness's durable signing key: a 32-byte seed file. Created
    once (atomically, mode 0600) if absent; loaded verbatim after."""
    p = Path(path)
    if p.exists():
        seed = p.read_bytes()
        if len(seed) != 32:
            raise ValueError(f"{p}: witness seed must be exactly 32 bytes")
        return Ed25519PrivateKey.from_private_bytes(seed)
    key = Ed25519PrivateKey.generate()
    from cryptography.hazmat.primitives import serialization as _ser
    seed = key.private_bytes(_ser.Encoding.Raw, _ser.PrivateFormat.Raw,
                             _ser.NoEncryption())
    p.parent.mkdir(parents=True, exist_ok=True)
    tmp = p.with_suffix(p.suffix + ".tmp")
    fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
    try:
        os.write(fd, seed)
        os.fsync(fd)
    finally:
        os.close(fd)
    os.replace(tmp, p)
    return key


def save_qualified(path: str | Path, qualified: dict) -> None:
    """Persist {witness_id(bytes): Ed25519PublicKey} as the registry."""
    out = {}
    for wid, pub in qualified.items():
        raw = pub.public_bytes_raw() if hasattr(pub, "public_bytes_raw") \
            else pub
        out[wid.decode("utf-8")] = raw.hex()
    p = Path(path)
    tmp = p.with_suffix(p.suffix + ".tmp")
    tmp.write_text(json.dumps(out, indent=1, sort_keys=True))
    os.replace(tmp, p)


def load_qualified(path: str | Path) -> dict:
    """Load the qualified-witness registry back to
    {witness_id(bytes): Ed25519PublicKey}."""
    raw = json.loads(Path(path).read_text())
    return {wid.encode("utf-8"):
            Ed25519PublicKey.from_public_bytes(bytes.fromhex(hexpub))
            for wid, hexpub in raw.items()}
