"""
InsAIts SDK - Tamper-Evident Audit Logger
==========================================
JSONL audit log with SHA-256 hash chain for tamper detection.

This is the SDK-side audit logger (separate from the API's
services/audit_logger.py which logs to Supabase).

Security properties:
    - Hash chain: each entry includes hash of previous entry
    - Content never logged: only message hashes (SHA-256 truncated)
    - Append-only: entries are only appended, never modified
    - Tamper verification: verify_integrity() detects any modification

Log format (JSONL):
    {"seq": 1, "ts": "2025-01-01T...", "event": "message_processed",
     "sender": "agent1", "receiver": "agent2", "had_anomaly": true,
     "severity": "high", "action": "processed", "msg_hash": "a1b2c3...",
     "correlation_id": "corr-123", "prev_hash": "0"*64, "hash": "abc..."}
"""

import hashlib
import json
import logging
import os
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional

logger = logging.getLogger(__name__)


# Genesis hash for the first entry in the chain
GENESIS_HASH = "0" * 64


class AuditLogger:
    """Tamper-evident JSONL audit logger with hash chain.

    Every log entry includes a SHA-256 hash that chains to the
    previous entry, making any modification detectable.

    Usage:
        audit = AuditLogger("./insaits_audit.jsonl")
        audit.write(
            event_type="message_processed",
            sender="agent1",
            receiver="agent2",
            had_anomaly=True,
            severity="high",
            action_taken="processed",
            message_hash="a1b2c3d4",
        )
        assert audit.verify_integrity()
    """

    def __init__(self, log_path: str = "./insaits_audit.jsonl"):
        """Initialize the audit logger.

        Args:
            log_path: Path to the JSONL audit file
        """
        self._path = Path(log_path)
        self._seq = 0
        self._prev_hash = GENESIS_HASH

        # Resume from existing file if present
        self._load_last_hash()

    def write(
        self,
        event_type: str,
        sender: str,
        receiver: str,
        had_anomaly: bool,
        severity: str,
        action_taken: str,
        message_hash: str,
        correlation_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Append a tamper-evident entry to the audit log.

        Message content is NEVER logged -- only the hash.

        Args:
            event_type: Event identifier (e.g. "message_processed")
            sender: Sending agent ID
            receiver: Receiving agent ID
            had_anomaly: Whether anomalies were detected
            severity: Highest severity level
            action_taken: What was done (e.g. "processed", "quarantined")
            message_hash: SHA-256 hash of the message content (truncated)
            correlation_id: Optional trace ID

        Returns:
            The complete log entry dict
        """
        self._seq += 1

        import time as _time_mod
        entry: Dict[str, Any] = {
            "seq": self._seq,
            "ts": datetime.now(timezone.utc).isoformat(),
            "timestamp_us": _time_mod.time_ns() // 1000,
            "event": event_type,
            "sender": sender,
            "receiver": receiver,
            "had_anomaly": had_anomaly,
            "severity": severity,
            "action": action_taken,
            "msg_hash": message_hash,
            "prev_hash": self._prev_hash,
        }

        if correlation_id is not None:
            entry["correlation_id"] = correlation_id

        # Compute hash of this entry (excluding the hash field itself)
        entry_hash = self._compute_hash(entry)
        entry["hash"] = entry_hash
        self._prev_hash = entry_hash

        # Append to file
        try:
            self._path.parent.mkdir(parents=True, exist_ok=True)
            with open(self._path, "a", encoding="utf-8") as f:
                f.write(json.dumps(entry, default=str) + "\n")
        except (OSError, PermissionError) as exc:
            logger.error("Audit log write failed: %s", exc)

        return entry

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

        Returns:
            True if all entries are intact and no tampering detected.
            False if any entry has been modified or is out of order.
        """
        if not self._path.exists():
            return True

        prev_hash = GENESIS_HASH

        try:
            with open(self._path, "r", encoding="utf-8") as f:
                for line_num, line in enumerate(f, 1):
                    line = line.strip()
                    if not line:
                        continue

                    try:
                        entry = json.loads(line)
                    except json.JSONDecodeError:
                        logger.error(
                            "Audit integrity: invalid JSON at line %d",
                            line_num,
                        )
                        return False

                    # Verify prev_hash chain
                    if entry.get("prev_hash") != prev_hash:
                        logger.error(
                            "Audit integrity: broken chain at line %d "
                            "(expected prev_hash=%s, got %s)",
                            line_num,
                            prev_hash[:16],
                            entry.get("prev_hash", "missing")[:16],
                        )
                        return False

                    # Recompute hash
                    stored_hash = entry.pop("hash", None)
                    computed_hash = self._compute_hash(entry)

                    if stored_hash != computed_hash:
                        logger.error(
                            "Audit integrity: tampered entry at line %d "
                            "(hash mismatch)",
                            line_num,
                        )
                        return False

                    prev_hash = stored_hash

        except (OSError, PermissionError) as exc:
            logger.error("Audit integrity check failed: %s", exc)
            return False

        return True

    def _compute_hash(self, entry: Dict[str, Any]) -> str:
        """Compute SHA-256 hash of an entry dict.

        The entry must NOT contain the 'hash' key when computing.

        Args:
            entry: Log entry dict (without 'hash' key)

        Returns:
            Hex-encoded SHA-256 hash
        """
        canonical = json.dumps(entry, sort_keys=True, default=str)
        return hashlib.sha256(canonical.encode("utf-8")).hexdigest()

    def _load_last_hash(self) -> None:
        """Resume hash chain from existing audit file."""
        if not self._path.exists():
            return

        try:
            last_line = None
            with open(self._path, "r", encoding="utf-8") as f:
                for line in f:
                    stripped = line.strip()
                    if stripped:
                        last_line = stripped

            if last_line is not None:
                entry = json.loads(last_line)
                self._prev_hash = entry.get("hash", GENESIS_HASH)
                self._seq = entry.get("seq", 0)

        except (OSError, json.JSONDecodeError, KeyError) as exc:
            logger.warning(
                "Could not resume audit chain: %s. Starting fresh.", exc
            )
            self._prev_hash = GENESIS_HASH
            self._seq = 0
