"""Immutable hash-chained audit log for SR 11-7 compliance (ADR-008).

Every operation is recorded in an append-only log where each entry's
hash includes the previous entry's hash, creating a tamper-evident chain.

All stdlib only (ADR-007).
"""

from __future__ import annotations

import hashlib
import json
import time
from dataclasses import dataclass


@dataclass(frozen=True)
class AuditEntry:
    """A single entry in the hash-chained audit log."""

    sequence: int
    timestamp: float
    action: str
    actor: str
    details: str = ""
    prev_hash: str = "genesis"
    entry_hash: str = ""

    def compute_hash(self) -> str:
        """Compute SHA-256 hash of this entry (excluding entry_hash field)."""
        data = json.dumps(
            {
                "sequence": self.sequence,
                "timestamp": self.timestamp,
                "action": self.action,
                "actor": self.actor,
                "details": self.details,
                "prev_hash": self.prev_hash,
            },
            sort_keys=True,
        )
        return hashlib.sha256(data.encode()).hexdigest()


class AuditLog:
    """Append-only, hash-chained audit log.

    Genesis block has prev_hash="genesis". Each subsequent entry
    chains to the previous entry's hash.
    """

    def __init__(self) -> None:
        self._entries: list[AuditEntry] = []

    def append(self, action: str, actor: str, details: str = "") -> AuditEntry:
        """Append a new entry to the audit log."""
        sequence = len(self._entries)
        prev_hash = self._entries[-1].entry_hash if self._entries else "genesis"
        timestamp = time.time()

        entry = AuditEntry(
            sequence=sequence,
            timestamp=timestamp,
            action=action,
            actor=actor,
            details=details,
            prev_hash=prev_hash,
        )

        entry_hash = entry.compute_hash()
        # Reconstruct with computed hash (frozen dataclass)
        entry = AuditEntry(
            sequence=entry.sequence,
            timestamp=entry.timestamp,
            action=entry.action,
            actor=entry.actor,
            details=entry.details,
            prev_hash=entry.prev_hash,
            entry_hash=entry_hash,
        )

        self._entries.append(entry)
        return entry

    def verify(self) -> bool:
        """Verify the integrity of the entire audit chain.

        Returns True if all hashes are valid and chain is unbroken.
        """
        for i, entry in enumerate(self._entries):
            # Verify hash
            expected_hash = AuditEntry(
                sequence=entry.sequence,
                timestamp=entry.timestamp,
                action=entry.action,
                actor=entry.actor,
                details=entry.details,
                prev_hash=entry.prev_hash,
            ).compute_hash()

            if entry.entry_hash != expected_hash:
                return False

            # Verify chain linkage
            if i == 0:
                if entry.prev_hash != "genesis":
                    return False
            else:
                if entry.prev_hash != self._entries[i - 1].entry_hash:
                    return False

        return True

    def search(self, action: str | None = None, actor: str | None = None) -> list[AuditEntry]:
        """Search entries by action and/or actor."""
        results = self._entries
        if action is not None:
            results = [e for e in results if e.action == action]
        if actor is not None:
            results = [e for e in results if e.actor == actor]
        return results

    def entries(self) -> list[AuditEntry]:
        """Return all entries (copy of the list)."""
        return list(self._entries)

    def __len__(self) -> int:
        return len(self._entries)
