"""Lightweight operator audit helpers.

v4.8 records sensitive/manual operator actions without adding a new table.
The durable audit stream is stored in business_events with event_type
``operator_action`` and, when a task_id is present, mirrored to task_events.
"""
from __future__ import annotations

import json
from typing import Any, Dict, Optional

from sqlalchemy import text

from app.db import engine


def _json(value: Any) -> str:
    return json.dumps(value or {}, ensure_ascii=False)


def record_operator_action(
    *,
    action: str,
    entity_type: str,
    entity_id: Optional[str] = None,
    actor: str = "operator",
    task_id: Optional[str] = None,
    opportunity_id: Optional[str] = None,
    conversation_id: Optional[str] = None,
    contact_id: Optional[str] = None,
    before: Optional[Dict[str, Any]] = None,
    after: Optional[Dict[str, Any]] = None,
    payload: Optional[Dict[str, Any]] = None,
) -> None:
    """Record a manual/sensitive operator action.

    Audit SQL invariant: event_type = 'operator_action'.

    This intentionally avoids a schema migration. ``business_events.task_id`` is
    nullable, so it can hold system/outbox/admin events as a generic audit log.
    """
    action = str(action or "operator_action").strip() or "operator_action"
    entity_type = str(entity_type or "unknown").strip() or "unknown"
    audit_payload = {
        "action": action,
        "entity_type": entity_type,
        "entity_id": str(entity_id or task_id or opportunity_id or ""),
        "actor": str(actor or "operator"),
        "opportunity_id": opportunity_id,
        "before": before or {},
        "after": after or {},
        **(payload or {}),
    }

    task_uuid = task_id if task_id else None
    with engine.begin() as conn:
        conn.execute(text("""
            INSERT INTO business_events (
                event_type,
                task_id,
                conversation_id,
                contact_id,
                payload,
                created_by
            )
            VALUES (
                'operator_action',
                CAST(:task_id AS UUID),
                :conversation_id,
                :contact_id,
                CAST(:payload AS JSONB),
                :created_by
            )
        """), {
            "task_id": task_uuid,
            "conversation_id": conversation_id,
            "contact_id": contact_id,
            "payload": _json(audit_payload),
            "created_by": str(actor or "operator"),
        })

        if task_id:
            conn.execute(text("""
                INSERT INTO task_events (
                    task_id,
                    event_type,
                    payload,
                    created_by
                )
                VALUES (
                    CAST(:task_id AS UUID),
                    'operator_action',
                    CAST(:payload AS JSONB),
                    :created_by
                )
            """), {
                "task_id": task_id,
                "payload": _json(audit_payload),
                "created_by": str(actor or "operator"),
            })


def record_operator_action_best_effort(**kwargs: Any) -> None:
    """Audit helper that must never break the operational action itself."""
    try:
        record_operator_action(**kwargs)
    except Exception as exc:  # pragma: no cover - defensive logging only
        print(f"ClientFlow operator audit failed: {exc}", flush=True)
