"""System entropy collection for timestamp plausibility witnesses."""

from __future__ import annotations

import hashlib
import os
import time
import uuid
from pathlib import Path

from soosef.federation.models import EntropyWitnesses

# Cache boot_id for the lifetime of the process (fallback only)
_cached_boot_id: str | None = None


def _read_proc_file(path: str) -> str | None:
    """Read a /proc file, returning None if unavailable."""
    try:
        return Path(path).read_text().strip()
    except OSError:
        return None


def _get_boot_id() -> str:
    """Get the kernel boot ID (Linux) or a per-process fallback."""
    global _cached_boot_id
    boot_id = _read_proc_file("/proc/sys/kernel/random/boot_id")
    if boot_id:
        return boot_id
    # Non-Linux fallback: stable per process lifetime
    if _cached_boot_id is None:
        _cached_boot_id = str(uuid.uuid4())
    return _cached_boot_id


def _get_proc_entropy() -> int:
    """Get kernel entropy pool availability (Linux) or a fallback marker."""
    value = _read_proc_file("/proc/sys/kernel/random/entropy_avail")
    if value is not None:
        try:
            return int(value)
        except ValueError:
            pass
    # Non-Linux fallback: always 32 (marker value)
    return len(os.urandom(32))


def _get_fs_snapshot(path: Path) -> bytes:
    """Hash filesystem metadata of the given path, truncated to 16 bytes.

    Includes mtime, ctime, size, and inode to capture any filesystem change.
    """
    try:
        st = path.stat()
        data = f"{st.st_mtime_ns}:{st.st_ctime_ns}:{st.st_size}:{st.st_ino}".encode()
    except OSError:
        # Path doesn't exist yet (first record) — hash the parent dir
        try:
            st = path.parent.stat()
            data = f"{st.st_mtime_ns}:{st.st_ctime_ns}:{st.st_size}:{st.st_ino}".encode()
        except OSError:
            data = b"no-fs-state"
    return hashlib.sha256(data).digest()[:16]


def collect_entropy_witnesses(chain_db_path: Path) -> EntropyWitnesses:
    """Gather system entropy witnesses for an attestation chain record.

    Args:
        chain_db_path: Path to chain.bin, used for fs_snapshot.

    Returns:
        EntropyWitnesses with current system state.
    """
    return EntropyWitnesses(
        sys_uptime=time.monotonic(),
        fs_snapshot=_get_fs_snapshot(chain_db_path),
        proc_entropy=_get_proc_entropy(),
        boot_id=_get_boot_id(),
    )
