"""
SQLAlchemy 2.0 ORM model for the immutable audit_events table.

Design:
  - Append-only: konstruct_app role has SELECT + INSERT only (enforced via REVOKE in migration)
  - Tenant-scoped via RLS — every query sees only the current tenant's rows
  - action_type discriminates between llm_call, tool_invocation, and escalation events

Important: The DB-level REVOKE UPDATE/DELETE on audit_events means that even if
application code accidentally attempts an UPDATE or DELETE, the database will reject it.
This is a hard compliance guarantee.
"""

from __future__ import annotations

import uuid
from datetime import datetime
from typing import Any

from sqlalchemy import DateTime, Integer, Text, func
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class AuditBase(DeclarativeBase):
    """Separate declarative base for audit models to avoid conflicts with tenant Base."""

    pass


class AuditEvent(AuditBase):
    """
    Immutable record of every LLM call, tool invocation, and escalation event.

    RLS is enabled — rows are scoped to the current tenant via app.current_tenant.
    The konstruct_app role has SELECT + INSERT only — UPDATE and DELETE are revoked.

    action_type values:
      'llm_call'        — LLM completion request/response
      'tool_invocation' — Tool execution (success or failure)
      'escalation'      — Agent handoff to human or another agent
    """

    __tablename__ = "audit_events"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        server_default=func.gen_random_uuid(),
    )
    tenant_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        nullable=False,
        index=True,
    )
    agent_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        nullable=True,
    )
    user_id: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Channel-native user identifier",
    )
    action_type: Mapped[str] = mapped_column(
        Text,
        nullable=False,
        comment="llm_call | tool_invocation | escalation",
    )
    input_summary: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Truncated input for audit readability (not full content)",
    )
    output_summary: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Truncated output for audit readability",
    )
    latency_ms: Mapped[int | None] = mapped_column(
        Integer,
        nullable=True,
        comment="Duration of the operation in milliseconds",
    )
    # NOTE: 'metadata' is reserved by SQLAlchemy's DeclarativeBase — the Python
    # attribute is named 'event_metadata' but maps to the 'metadata' DB column.
    # The AuditLogger uses raw SQL text() so this ORM attribute is for read queries only.
    event_metadata: Mapped[dict[str, Any]] = mapped_column(
        "metadata",
        JSONB,
        nullable=False,
        server_default="{}",
        default=dict,
        comment="Additional structured context (model name, tool args hash, etc.)",
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )

    def __repr__(self) -> str:
        return f"<AuditEvent id={self.id} action={self.action_type} tenant={self.tenant_id}>"
