"""
audit_gate_and_coordinator.py

The last two of the four governance components referenced throughout the
lab documents but never built as real code: AuditGate and
GovernanceCoordinator. GenesisBurnIn (genesis.py) and LatticeStateMachine
(lattice.py) were built and verified earlier; GovernanceLayer
(governance_layer.py) handles estimator aggregation. This file completes
the set.

AuditGate: intercepts every governance decision, produces an immutable
audit record for each one (regardless of outcome), and can VETO a
decision that an underlying engine would otherwise allow, if it fails
an independent audit rule -- a second, independent check, not just a
logger.

GovernanceCoordinator: orchestrates GenesisBurnIn, LatticeStateMachine,
AuditGate, and an underlying decision engine into one coherent flow, so
"the governance layer" is one real object other code can call, instead
of four separate pieces someone has to remember to wire together
correctly by hand.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import List, Callable, Any, Dict, Optional
import hashlib
import time

from genesis import GenesisBurnIn, GenesisState
from lattice import LatticeStateMachine, NodeState


@dataclass(frozen=True)
class AuditRecord:
    """Immutable -- once created, cannot be edited. Every governance
    decision produces exactly one of these, regardless of outcome."""
    decision_id: str
    timestamp: float
    decision_type: str
    upstream_outcome: str
    audit_verdict: str
    veto_reason: Optional[str]
    record_hash: str

    @staticmethod
    def create(decision_id, decision_type, upstream_outcome, audit_verdict, veto_reason):
        ts = time.time()
        payload = f"{decision_id}:{ts}:{decision_type}:{upstream_outcome}:{audit_verdict}:{veto_reason}"
        h = hashlib.sha256(payload.encode()).hexdigest()
        return AuditRecord(decision_id, ts, decision_type, upstream_outcome, audit_verdict, veto_reason, h)


class AuditGate:
    """An independent check that can override an upstream ACCEPT if it
    violates a rule the upstream engine doesn't itself check. This is
    what makes it a gate and not just a logger -- it has real veto power."""

    def __init__(self, veto_rules=None):
        self.veto_rules = veto_rules or []
        self.audit_log = []
        self._counter = 0

    def review(self, decision_type, upstream_outcome, context):
        self._counter += 1
        decision_id = f"audit-{self._counter:06d}"

        veto_reason = None
        for rule in self.veto_rules:
            reason = rule(context)
            if reason:
                veto_reason = reason
                break

        audit_verdict = "vetoed" if veto_reason else "confirmed"
        record = AuditRecord.create(decision_id, decision_type, upstream_outcome, audit_verdict, veto_reason)
        self.audit_log.append(record)
        return record


class GovernanceCoordinator:
    """Orchestrates GenesisBurnIn + LatticeStateMachine + AuditGate +
    an underlying decision engine into one object."""

    def __init__(self, decision_engine, genesis=None, lattice=None, audit_gate=None):
        self.decision_engine = decision_engine
        self.genesis = genesis or GenesisBurnIn()
        self.lattice = lattice or LatticeStateMachine()
        self.audit_gate = audit_gate or AuditGate()
        self.genesis_state = None

    def initialize(self, init_data):
        self.genesis_state = self.genesis.burn_in(init_data)
        if not self.genesis.verify(self.genesis_state):
            return False
        self.lattice.transition(NodeState.VERIFIED, "genesis burn-in verified")
        self.lattice.transition(NodeState.OPERATIONAL, "coordinator initialized")
        return True

    def coordinate_decision(self, decision_type, context):
        if not self.lattice.is_healthy():
            return {"final_decision": "REJECT", "reason": "lattice not in a healthy state", "audit": None}

        upstream = self.decision_engine(context)
        record = self.audit_gate.review(decision_type, upstream, context)

        final = "REJECT" if record.audit_verdict == "vetoed" else upstream
        return {"final_decision": final, "upstream": upstream, "audit": record}
