"""Device identity — one Ed25519 key per install, custody in the core and nowhere else.

The owner's ruling: keys are one of the things agents and connectors get NO access to.
So the key lives here, in the core's own data directory, created on first run with mode
0600 — and the only thing that ever leaves this module is the PUBLIC half and a signer
callable. No agent container mounts this path; no tool serves it; `edge_disclose` has no
field for it.

The seed file is 32 random bytes, hex-encoded. `cryptography` is the `sign` extra and is
imported lazily: an install that never signs never needs it, and `load_or_create`
answers `None` with the reason instead of crashing a node that only wanted to run.
"""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from typing import Callable

KEY_FILENAME = "device.key"


@dataclass(frozen=True)
class DeviceIdentity:
    """The install's signing identity: where the seed lives, and the public half."""

    seed_path: Path
    public_hex: str

    @property
    def signer(self) -> Callable[[bytes], bytes]:
        from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

        seed = bytes.fromhex(self.seed_path.read_text().strip())
        return Ed25519PrivateKey.from_private_bytes(seed).sign


def load_or_create(data_dir: str | os.PathLike) -> DeviceIdentity | None:
    """The identity at `data_dir`, created on first run. Returns None (and says why on
    stderr is the caller's job) when `cryptography` is absent — running unsigned is a
    valid participation; pretending to have an identity is not."""
    try:
        from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
    except ImportError:
        return None

    path = Path(data_dir) / KEY_FILENAME
    if not path.exists():
        path.parent.mkdir(parents=True, exist_ok=True)
        seed = os.urandom(32)
        descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
        with os.fdopen(descriptor, "w") as handle:
            handle.write(seed.hex() + "\n")
    else:
        os.chmod(path, 0o600)          # an existing key keeps its discipline too
        seed = bytes.fromhex(path.read_text().strip())

    public = Ed25519PrivateKey.from_private_bytes(seed).public_key()
    return DeviceIdentity(seed_path=path, public_hex=public.public_bytes_raw().hex())
