"""Append-only, hash-chained audit log (build plan Sections 5, 14).

``record`` appends one entry to a workspace's chain. ``verify_chain`` re-walks a
workspace's entries and confirms every hash and back-link, detecting tampering.
"""

from __future__ import annotations

import hashlib
import json
import uuid

from sqlalchemy import select
from sqlalchemy.orm import Session

from legate.db.models.audit import AuditLog


def _canonical(payload: dict) -> str:
    return json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str)


def _compute_hash(hash_prev: str | None, payload: dict) -> str:
    material = (hash_prev or "") + _canonical(payload)
    return hashlib.sha256(material.encode("utf-8")).hexdigest()


def record(
    db: Session,
    *,
    workspace_id: uuid.UUID,
    actor: str,
    action: str,
    target: str | None = None,
    metadata: dict | None = None,
) -> AuditLog:
    """Append an entry to ``workspace_id``'s chain and return it (not committed)."""
    last = db.scalars(
        select(AuditLog)
        .where(AuditLog.workspace_id == workspace_id)
        .order_by(AuditLog.seq.desc())
        .limit(1)
    ).first()

    seq = (last.seq + 1) if last else 1
    hash_prev = last.hash if last else None
    payload = {
        "workspace_id": str(workspace_id),
        "seq": seq,
        "actor": actor,
        "action": action,
        "target": target,
        "metadata": metadata or {},
    }
    entry = AuditLog(
        workspace_id=workspace_id,
        seq=seq,
        actor=actor,
        action=action,
        target=target,
        event_metadata=metadata or {},
        hash_prev=hash_prev,
        hash=_compute_hash(hash_prev, payload),
    )
    db.add(entry)
    db.flush()
    return entry


def verify_chain(db: Session, workspace_id: uuid.UUID) -> bool:
    """Return True iff every entry's hash and back-link are intact."""
    entries = db.scalars(
        select(AuditLog).where(AuditLog.workspace_id == workspace_id).order_by(AuditLog.seq.asc())
    ).all()

    prev_hash: str | None = None
    for entry in entries:
        payload = {
            "workspace_id": str(entry.workspace_id),
            "seq": entry.seq,
            "actor": entry.actor,
            "action": entry.action,
            "target": entry.target,
            "metadata": entry.event_metadata or {},
        }
        if entry.hash_prev != prev_hash:
            return False
        if entry.hash != _compute_hash(prev_hash, payload):
            return False
        prev_hash = entry.hash
    return True
