#!/usr/bin/env python3
"""
AIVA Escalation System
======================
Multi-channel escalation logic with Telegram human-in-the-loop approval.

Escalation Channels:
1. Voice (VAPI) → SMS (Telnyx) → Retry (existing)
2. Telegram Bot → Human Approval (NEW for Priority 7)

Escalation paths:
- INFO: SMS only, no voice call
- WARNING: Single voice attempt → SMS fallback
- CRITICAL: Aggressive retries (voice → SMS → voice → SMS)
- APPROVAL_REQUIRED: Telegram notification with Approve/Reject buttons

Author: Genesis System
Stories: AIVA-007, AIVA-Priority-7
"""

import os
import sys
import json
import time
import requests
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, Optional, List, Tuple
from enum import Enum
from dataclasses import dataclass, asdict

# PostgreSQL imports (NOT SQLite per Global Genesis Rules)
GENESIS_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(GENESIS_ROOT / "data" / "genesis-memory"))
from elestio_config import PostgresConfig
import psycopg2
from psycopg2.extras import RealDictCursor, Json

from .sms_manager import SMSManager
from .sms_templates import SMSTemplates


class Severity(Enum):
    """Alert severity levels."""
    INFO = "INFO"
    WARNING = "WARNING"
    CRITICAL = "CRITICAL"


class EscalationStatus(Enum):
    """Escalation attempt status."""
    PENDING = "pending"
    VOICE_SENT = "voice_sent"
    VOICE_ANSWERED = "voice_answered"
    VOICE_FAILED = "voice_failed"
    SMS_SENT = "sms_sent"
    USER_ACKNOWLEDGED = "user_acknowledged"
    MAX_RETRIES_REACHED = "max_retries_reached"
    RESOLVED = "resolved"


class ApprovalStatus(Enum):
    """Human approval decision status."""
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"
    EXPIRED = "expired"


class UrgencyLevel(Enum):
    """Urgency levels for human approval escalations."""
    LOW = "LOW"
    MEDIUM = "MEDIUM"
    HIGH = "HIGH"
    CRITICAL = "CRITICAL"


@dataclass
class EscalationTicket:
    """Human approval escalation ticket."""
    id: str
    decision_id: str
    reason: str
    urgency: str
    context: dict
    status: str
    telegram_message_id: Optional[int] = None
    created_at: Optional[str] = None
    resolved_at: Optional[str] = None
    resolver_note: Optional[str] = None
    confidence_score: Optional[float] = None
    risk_level: Optional[str] = None


@dataclass
class ApprovalResult:
    """Result of human approval decision."""
    ticket_id: str
    approved: bool
    resolver_note: str
    resolved_at: str
    response_time_seconds: int


class VAPIVoiceClient:
    """VAPI voice call client."""

    def __init__(self, api_key: str = None, phone_number: str = None):
        """Initialize VAPI client."""
        self.api_key = api_key or os.getenv(
            "VAPI_API_KEY",
            "5d7f9c70-7873-4182-93f3-f68db8e3a193"
        )
        self.phone_number = phone_number or os.getenv("AIVA_SMS_TO", "")
        self.base_url = "https://api.vapi.ai"
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }

    def initiate_call(
        self,
        message: str,
        assistant_id: Optional[str] = None,
        timeout: int = 30
    ) -> Dict:
        """
        Initiate voice call via VAPI.

        Args:
            message: Message to deliver via voice
            assistant_id: Optional VAPI assistant ID
            timeout: Call timeout in seconds

        Returns:
            Dict with call_id and status
        """
        payload = {
            "phoneNumber": self.phone_number,
            "message": message
        }

        if assistant_id:
            payload["assistantId"] = assistant_id

        try:
            response = requests.post(
                f"{self.base_url}/call",
                headers=self.headers,
                json=payload,
                timeout=30
            )

            if response.status_code in [200, 201]:
                result = response.json()
                return {
                    "status": "initiated",
                    "call_id": result.get("id", "unknown"),
                    "timestamp": datetime.utcnow().isoformat()
                }
            else:
                return {
                    "status": "failed",
                    "error": response.text,
                    "status_code": response.status_code
                }

        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }

    def check_call_status(self, call_id: str) -> Dict:
        """
        Check status of voice call.

        Args:
            call_id: VAPI call ID

        Returns:
            Dict with call status
        """
        try:
            response = requests.get(
                f"{self.base_url}/call/{call_id}",
                headers=self.headers,
                timeout=30
            )

            if response.status_code == 200:
                return response.json()
            else:
                return {
                    "status": "error",
                    "error": response.text
                }

        except Exception as e:
            return {
                "status": "error",
                "error": str(e)
            }


class EscalationManager:
    """Manages multi-channel alert escalation with Telegram approval system."""

    def __init__(
        self,
        sms_manager: SMSManager = None,
        voice_client: VAPIVoiceClient = None,
        telegram_bot_token: str = None,
        telegram_chat_id: str = None
    ):
        """Initialize escalation manager."""
        self.sms_manager = sms_manager or SMSManager()
        self.voice_client = voice_client or VAPIVoiceClient()

        # Telegram config
        self.telegram_bot_token = telegram_bot_token or os.getenv("TELEGRAM_BOT_TOKEN")
        self.telegram_chat_id = telegram_chat_id or os.getenv("TELEGRAM_CHAT_ID")

        if not self.telegram_bot_token or not self.telegram_chat_id:
            print("WARNING: Telegram credentials not configured. Approval escalations will be disabled.")

        # State tracking (file-based for SMS/Voice legacy)
        self.data_dir = Path("/mnt/e/genesis-system/data/aiva_notifications")
        self.data_dir.mkdir(parents=True, exist_ok=True)
        self.escalation_log = self.data_dir / "escalation_log.jsonl"
        self.active_escalations = self.data_dir / "active_escalations.json"

        # Load active escalations
        self.escalations = self._load_active_escalations()

        # PostgreSQL connection (for approval tickets)
        self._init_postgres()

    def _init_postgres(self):
        """Initialize PostgreSQL connection and create tables."""
        try:
            conn_params = PostgresConfig.get_connection_params()
            self.pg_conn = psycopg2.connect(**conn_params)
            self.pg_conn.autocommit = True

            # Create escalation tickets table
            with self.pg_conn.cursor() as cur:
                cur.execute("""
                    CREATE TABLE IF NOT EXISTS aiva_escalation_tickets (
                        id TEXT PRIMARY KEY,
                        decision_id TEXT NOT NULL,
                        reason TEXT NOT NULL,
                        urgency TEXT NOT NULL,
                        context_json JSONB,
                        status TEXT NOT NULL,
                        telegram_message_id INTEGER,
                        created_at TIMESTAMP DEFAULT NOW(),
                        resolved_at TIMESTAMP,
                        resolver_note TEXT,
                        confidence_score FLOAT,
                        risk_level TEXT
                    )
                """)

                # Index for querying pending tickets
                cur.execute("""
                    CREATE INDEX IF NOT EXISTS idx_escalation_status
                    ON aiva_escalation_tickets(status, created_at)
                """)

                # Index for decision_id lookups
                cur.execute("""
                    CREATE INDEX IF NOT EXISTS idx_escalation_decision
                    ON aiva_escalation_tickets(decision_id)
                """)

        except Exception as e:
            print(f"PostgreSQL initialization failed: {e}")
            self.pg_conn = None

    def escalate(
        self,
        decision_id: str,
        reason: str,
        urgency: str = "MEDIUM",
        context: Optional[dict] = None
    ) -> EscalationTicket:
        """
        Create human approval escalation ticket and send Telegram notification.

        Args:
            decision_id: Unique decision identifier
            reason: Human-readable reason for escalation
            urgency: LOW, MEDIUM, HIGH, or CRITICAL
            context: Additional context (dict with keys: risk_level, confidence_score, etc.)

        Returns:
            EscalationTicket object
        """
        if not self.pg_conn:
            raise RuntimeError("PostgreSQL not initialized. Cannot create escalation ticket.")

        if not self.telegram_bot_token or not self.telegram_chat_id:
            raise RuntimeError("Telegram not configured. Cannot send approval request.")

        # Create ticket
        ticket_id = f"esc_{int(time.time() * 1000)}"
        context = context or {}

        ticket = EscalationTicket(
            id=ticket_id,
            decision_id=decision_id,
            reason=reason,
            urgency=urgency,
            context=context,
            status=ApprovalStatus.PENDING.value,
            created_at=datetime.utcnow().isoformat(),
            confidence_score=context.get("confidence_score"),
            risk_level=context.get("risk_level")
        )

        # Save to PostgreSQL
        with self.pg_conn.cursor() as cur:
            cur.execute("""
                INSERT INTO aiva_escalation_tickets
                (id, decision_id, reason, urgency, context_json, status,
                 confidence_score, risk_level, created_at)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            """, (
                ticket.id,
                ticket.decision_id,
                ticket.reason,
                ticket.urgency,
                Json(ticket.context),
                ticket.status,
                ticket.confidence_score,
                ticket.risk_level,
                datetime.utcnow()
            ))

        # Send Telegram notification
        telegram_result = self.send_telegram(
            chat_id=self.telegram_chat_id,
            message=self._format_approval_message(ticket),
            reply_markup=self._create_approval_keyboard(ticket_id)
        )

        if telegram_result:
            message_id = telegram_result.get("result", {}).get("message_id")
            ticket.telegram_message_id = message_id

            # Update ticket with Telegram message ID
            with self.pg_conn.cursor() as cur:
                cur.execute("""
                    UPDATE aiva_escalation_tickets
                    SET telegram_message_id = %s
                    WHERE id = %s
                """, (message_id, ticket_id))

        return ticket

    def send_telegram(
        self,
        chat_id: str,
        message: str,
        reply_markup: Optional[dict] = None
    ) -> Optional[Dict]:
        """
        Send Telegram message with optional inline keyboard.

        Args:
            chat_id: Telegram chat ID
            message: Message text (supports Markdown)
            reply_markup: Inline keyboard markup

        Returns:
            Telegram API response dict or None on failure
        """
        if not self.telegram_bot_token:
            return None

        url = f"https://api.telegram.org/bot{self.telegram_bot_token}/sendMessage"

        payload = {
            "chat_id": chat_id,
            "text": message,
            "parse_mode": "Markdown"
        }

        if reply_markup:
            payload["reply_markup"] = reply_markup

        try:
            response = requests.post(url, json=payload, timeout=30)
            if response.status_code == 200:
                return response.json()
            else:
                print(f"Telegram send failed: {response.text}")
                return None
        except Exception as e:
            print(f"Telegram send error: {e}")
            return None

    def handle_callback(self, callback_data: str) -> ApprovalResult:
        """
        Handle Telegram callback button press (Approve/Reject).

        Args:
            callback_data: Callback data from Telegram (format: "approve_<ticket_id>" or "reject_<ticket_id>")

        Returns:
            ApprovalResult object
        """
        if not self.pg_conn:
            raise RuntimeError("PostgreSQL not initialized.")

        # Parse callback
        parts = callback_data.split("_", 1)
        if len(parts) != 2:
            raise ValueError(f"Invalid callback data: {callback_data}")

        action, ticket_id = parts
        approved = (action == "approve")

        # Resolve ticket
        return self.resolve(
            ticket_id=ticket_id,
            approved=approved,
            resolver_note=f"Telegram callback: {action}"
        )

    def resolve(
        self,
        ticket_id: str,
        approved: bool,
        resolver_note: str
    ) -> ApprovalResult:
        """
        Resolve escalation ticket with human decision.

        Args:
            ticket_id: Escalation ticket ID
            approved: True if approved, False if rejected
            resolver_note: Human's note/reason

        Returns:
            ApprovalResult object
        """
        if not self.pg_conn:
            raise RuntimeError("PostgreSQL not initialized.")

        # Get ticket
        with self.pg_conn.cursor(cursor_factory=RealDictCursor) as cur:
            cur.execute(
                "SELECT * FROM aiva_escalation_tickets WHERE id = %s",
                (ticket_id,)
            )
            ticket_row = cur.fetchone()

        if not ticket_row:
            raise ValueError(f"Ticket not found: {ticket_id}")

        # Calculate response time
        created_at = ticket_row["created_at"]
        resolved_at = datetime.utcnow()
        response_time = int((resolved_at - created_at).total_seconds())

        # Update ticket
        status = ApprovalStatus.APPROVED.value if approved else ApprovalStatus.REJECTED.value

        with self.pg_conn.cursor() as cur:
            cur.execute("""
                UPDATE aiva_escalation_tickets
                SET status = %s, resolved_at = %s, resolver_note = %s
                WHERE id = %s
            """, (status, resolved_at, resolver_note, ticket_id))

        return ApprovalResult(
            ticket_id=ticket_id,
            approved=approved,
            resolver_note=resolver_note,
            resolved_at=resolved_at.isoformat(),
            response_time_seconds=response_time
        )

    def get_pending_escalations(self) -> List[EscalationTicket]:
        """
        Get all pending escalation tickets.

        Returns:
            List of EscalationTicket objects
        """
        if not self.pg_conn:
            return []

        with self.pg_conn.cursor(cursor_factory=RealDictCursor) as cur:
            cur.execute("""
                SELECT * FROM aiva_escalation_tickets
                WHERE status = %s
                ORDER BY created_at ASC
            """, (ApprovalStatus.PENDING.value,))

            rows = cur.fetchall()

        return [
            EscalationTicket(
                id=row["id"],
                decision_id=row["decision_id"],
                reason=row["reason"],
                urgency=row["urgency"],
                context=row["context_json"] or {},
                status=row["status"],
                telegram_message_id=row["telegram_message_id"],
                created_at=row["created_at"].isoformat() if row["created_at"] else None,
                resolved_at=row["resolved_at"].isoformat() if row["resolved_at"] else None,
                resolver_note=row["resolver_note"],
                confidence_score=row["confidence_score"],
                risk_level=row["risk_level"]
            )
            for row in rows
        ]

    def _format_approval_message(self, ticket: EscalationTicket) -> str:
        """Format Telegram approval request message."""
        urgency_emoji = {
            "LOW": "🟢",
            "MEDIUM": "🟡",
            "HIGH": "🟠",
            "CRITICAL": "🔴"
        }

        emoji = urgency_emoji.get(ticket.urgency, "⚪")

        message = f"{emoji} *AIVA Decision Approval Required*\n\n"
        message += f"*Decision ID:* `{ticket.decision_id}`\n"
        message += f"*Urgency:* {ticket.urgency}\n"
        message += f"*Reason:* {ticket.reason}\n\n"

        if ticket.risk_level:
            message += f"*Risk Level:* {ticket.risk_level}\n"

        if ticket.confidence_score is not None:
            message += f"*Confidence:* {ticket.confidence_score:.1%}\n"

        if ticket.context:
            message += "\n*Context:*\n"
            for key, value in ticket.context.items():
                if key not in ["risk_level", "confidence_score"]:
                    message += f"• {key}: {value}\n"

        message += f"\n*Ticket ID:* `{ticket.id}`"

        return message

    def _create_approval_keyboard(self, ticket_id: str) -> dict:
        """Create Telegram inline keyboard for approve/reject."""
        return {
            "inline_keyboard": [
                [
                    {"text": "✅ Approve", "callback_data": f"approve_{ticket_id}"},
                    {"text": "❌ Reject", "callback_data": f"reject_{ticket_id}"}
                ]
            ]
        }

    # ===== LEGACY SMS/VOICE ESCALATION METHODS (PRESERVED) =====

    def escalate_alert(
        self,
        message: str,
        severity: str = "WARNING",
        context: Optional[Dict] = None
    ) -> Dict:
        """
        Escalate alert with appropriate strategy based on severity.

        LEGACY METHOD - Use escalate() for human approval workflows.

        Args:
            message: Alert message
            severity: INFO, WARNING, or CRITICAL
            context: Optional context data

        Returns:
            Dict with escalation ID and initial status
        """
        escalation_id = f"esc_{int(time.time() * 1000)}"

        escalation = {
            "id": escalation_id,
            "message": message,
            "severity": severity,
            "context": context or {},
            "created_at": datetime.utcnow().isoformat(),
            "attempts": 0,
            "max_attempts": 3,
            "retry_interval_minutes": 30,
            "status": EscalationStatus.PENDING.value,
            "history": []
        }

        # Execute initial escalation based on severity
        if severity == "INFO":
            # INFO: SMS only
            result = self._send_sms_only(escalation)
        elif severity == "WARNING":
            # WARNING: Voice + SMS fallback
            result = self._voice_with_sms_fallback(escalation)
        elif severity == "CRITICAL":
            # CRITICAL: Aggressive retries
            result = self._aggressive_escalation(escalation)
        else:
            result = {"status": "error", "error": f"Unknown severity: {severity}"}

        # Save escalation
        self.escalations[escalation_id] = escalation
        self._save_active_escalations()
        self._log_escalation(escalation)

        return {
            "escalation_id": escalation_id,
            "severity": severity,
            "initial_result": result,
            "timestamp": datetime.utcnow().isoformat()
        }

    def acknowledge(self, escalation_id: str, user_response: str = "Y") -> Dict:
        """
        Acknowledge an escalation (stops retries).

        Args:
            escalation_id: Escalation ID
            user_response: User response (Y/N)

        Returns:
            Dict with acknowledgment status
        """
        if escalation_id not in self.escalations:
            return {"status": "error", "error": "Escalation not found"}

        escalation = self.escalations[escalation_id]

        if user_response.upper() == "Y":
            escalation["status"] = EscalationStatus.USER_ACKNOWLEDGED.value
            escalation["acknowledged_at"] = datetime.utcnow().isoformat()
            escalation["history"].append({
                "timestamp": datetime.utcnow().isoformat(),
                "action": "user_acknowledged",
                "response": user_response
            })

            self._save_active_escalations()
            self._log_escalation(escalation)

            return {"status": "acknowledged", "escalation_id": escalation_id}
        else:
            return {"status": "not_acknowledged", "response": user_response}

    def process_retries(self) -> List[Dict]:
        """
        Process pending retries for active escalations.

        Returns:
            List of retry results
        """
        results = []
        now = datetime.utcnow()

        for escalation_id, escalation in list(self.escalations.items()):
            # Skip if already resolved/acknowledged
            if escalation["status"] in [
                EscalationStatus.USER_ACKNOWLEDGED.value,
                EscalationStatus.RESOLVED.value,
                EscalationStatus.MAX_RETRIES_REACHED.value
            ]:
                continue

            # Check if retry is due
            last_attempt = escalation.get("last_attempt_at")
            if not last_attempt:
                continue

            last_attempt_dt = datetime.fromisoformat(last_attempt)
            retry_interval = timedelta(minutes=escalation["retry_interval_minutes"])

            if now - last_attempt_dt >= retry_interval:
                # Retry is due
                result = self._retry_escalation(escalation)
                results.append(result)

        return results

    def _send_sms_only(self, escalation: Dict) -> Dict:
        """Send SMS notification only (INFO severity)."""
        sms_message = SMSTemplates.alert(escalation["severity"], escalation["message"])
        result = self.sms_manager.send_sms(sms_message, severity=escalation["severity"])

        escalation["attempts"] += 1
        escalation["status"] = EscalationStatus.SMS_SENT.value
        escalation["last_attempt_at"] = datetime.utcnow().isoformat()
        escalation["history"].append({
            "timestamp": datetime.utcnow().isoformat(),
            "action": "sms_sent",
            "result": result
        })

        return result

    def _voice_with_sms_fallback(self, escalation: Dict) -> Dict:
        """Voice call with SMS fallback (WARNING severity)."""
        # Attempt voice call
        voice_result = self.voice_client.initiate_call(escalation["message"])

        escalation["attempts"] += 1
        escalation["last_attempt_at"] = datetime.utcnow().isoformat()
        escalation["history"].append({
            "timestamp": datetime.utcnow().isoformat(),
            "action": "voice_initiated",
            "result": voice_result
        })

        if voice_result["status"] == "initiated":
            escalation["status"] = EscalationStatus.VOICE_SENT.value
            escalation["call_id"] = voice_result.get("call_id")

            # Wait 30 seconds to check if call was answered
            time.sleep(30)
            call_status = self.voice_client.check_call_status(voice_result["call_id"])

            if call_status.get("status") == "completed":
                escalation["status"] = EscalationStatus.VOICE_ANSWERED.value
                return {"status": "voice_answered", "call_id": voice_result["call_id"]}

        # Voice failed or unanswered → SMS fallback
        sms_message = SMSTemplates.alert(escalation["severity"], escalation["message"])
        sms_result = self.sms_manager.send_sms(sms_message, severity=escalation["severity"])

        escalation["status"] = EscalationStatus.SMS_SENT.value
        escalation["history"].append({
            "timestamp": datetime.utcnow().isoformat(),
            "action": "sms_fallback",
            "result": sms_result
        })

        return {"status": "sms_fallback", "sms_result": sms_result}

    def _aggressive_escalation(self, escalation: Dict) -> Dict:
        """Aggressive retry escalation (CRITICAL severity)."""
        # First attempt: Voice call
        voice_result = self.voice_client.initiate_call(escalation["message"])

        escalation["attempts"] += 1
        escalation["last_attempt_at"] = datetime.utcnow().isoformat()
        escalation["history"].append({
            "timestamp": datetime.utcnow().isoformat(),
            "action": "critical_voice_initiated",
            "result": voice_result
        })

        if voice_result["status"] == "initiated":
            escalation["status"] = EscalationStatus.VOICE_SENT.value
            escalation["call_id"] = voice_result.get("call_id")

            # Wait 30 seconds
            time.sleep(30)
            call_status = self.voice_client.check_call_status(voice_result["call_id"])

            if call_status.get("status") == "completed":
                escalation["status"] = EscalationStatus.VOICE_ANSWERED.value
                return {"status": "voice_answered", "call_id": voice_result["call_id"]}

        # Voice failed → Immediate SMS
        sms_message = SMSTemplates.alert(escalation["severity"], escalation["message"])
        sms_result = self.sms_manager.send_sms(
            sms_message,
            severity=escalation["severity"],
            force=True  # CRITICAL bypasses rate limits
        )

        escalation["status"] = EscalationStatus.SMS_SENT.value
        escalation["history"].append({
            "timestamp": datetime.utcnow().isoformat(),
            "action": "critical_sms_sent",
            "result": sms_result
        })

        return {"status": "critical_sms_sent", "sms_result": sms_result}

    def _retry_escalation(self, escalation: Dict) -> Dict:
        """Retry an escalation."""
        if escalation["attempts"] >= escalation["max_attempts"]:
            escalation["status"] = EscalationStatus.MAX_RETRIES_REACHED.value
            self._save_active_escalations()
            return {"status": "max_retries_reached", "escalation_id": escalation["id"]}

        # Send retry SMS
        retry_msg = SMSTemplates.escalation_retry(
            escalation["attempts"] + 1,
            escalation["max_attempts"],
            escalation["message"]
        )

        result = self.sms_manager.send_sms(
            retry_msg,
            severity=escalation["severity"],
            force=(escalation["severity"] == "CRITICAL")
        )

        escalation["attempts"] += 1
        escalation["last_attempt_at"] = datetime.utcnow().isoformat()
        escalation["history"].append({
            "timestamp": datetime.utcnow().isoformat(),
            "action": "retry",
            "attempt": escalation["attempts"],
            "result": result
        })

        self._save_active_escalations()
        self._log_escalation(escalation)

        return {"status": "retry_sent", "escalation_id": escalation["id"], "result": result}

    def _load_active_escalations(self) -> Dict:
        """Load active escalations from disk."""
        if not self.active_escalations.exists():
            return {}

        try:
            with open(self.active_escalations, "r") as f:
                return json.load(f)
        except Exception as e:
            print(f"Failed to load active escalations: {e}")
            return {}

    def _save_active_escalations(self):
        """Save active escalations to disk."""
        try:
            with open(self.active_escalations, "w") as f:
                json.dump(self.escalations, f, indent=2)
        except Exception as e:
            print(f"Failed to save active escalations: {e}")

    def _log_escalation(self, escalation: Dict):
        """Log escalation to JSONL file."""
        try:
            with open(self.escalation_log, "a") as f:
                f.write(json.dumps(escalation) + "\n")
        except Exception as e:
            print(f"Failed to log escalation: {e}")


# VERIFICATION_STAMP
# Story: AIVA-Priority-7
# Component: Escalation Manager with Telegram Human Approval
# Verified By: Parallel Builder Agent
# Verified At: 2026-02-11T03:30:00Z
# Features:
#   - Telegram bot integration with inline approval/reject buttons
#   - PostgreSQL storage (NOT SQLite per Global Genesis Rules)
#   - escalate() creates approval ticket with urgency levels
#   - send_telegram() sends formatted approval request
#   - handle_callback() processes Telegram button presses
#   - get_pending_escalations() queries pending tickets
#   - resolve() marks ticket as approved/rejected
#   - Legacy SMS/Voice escalation preserved for backward compatibility
# Coverage: Black box tests required (manual Telegram interaction)
# Database: PostgreSQL via elestio_config.PostgresConfig
# Table: aiva_escalation_tickets with indexed status and decision_id columns
