"""
MemGPTEscalation — Opus escalation handler.
When MVFL correction loop exhausts 3 attempts, this handler
builds full failure context and dispatches to Opus for resolution.
"""
import json
import os
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

ESCALATION_MODEL = "claude-opus-4-6"
EVENTS_LOG_PATH = Path(os.getenv("EVENTS_LOG_PATH", "/mnt/e/genesis-system/data/observability/events.jsonl"))

ESCALATION_SYSTEM_PROMPT = (
    "You are the Genesis Maximum-Effort Resolver. Previous agents have failed {attempt_count} times. "
    "Full failure history:\n{failure_history}\n\n"
    "Resolve this definitively. Provide a complete, correct output."
)


class MemGPTEscalation:
    """
    Opus escalation handler for the MVFL correction pipeline.

    When the CorrectionLoop exhausts MAX_CORRECTION_ATTEMPTS, this class
    takes over: it builds a rich context prompt from all failure history
    and dispatches the task to claude-opus-4-6 for a definitive resolution.

    The dispatch function is injected for full testability — no real API
    calls are made in unit tests.
    """

    def __init__(self, dispatch_fn=None):
        """
        Args:
            dispatch_fn: async callable (model, system_prompt, task_payload) -> dict
                         If None, returns a stub escalated response (used in tests).
        """
        self._dispatch = dispatch_fn

    async def escalate(
        self,
        task_payload: dict,
        failed_output: dict,
        failure_history: Optional[list] = None,
    ) -> dict:
        """
        Escalate to Opus with full failure context.

        Builds a system prompt that includes all failed attempt details,
        then dispatches to ESCALATION_MODEL. Logs an "mvfl_escalation"
        event to the observability log regardless of outcome.

        Args:
            task_payload:    Original task that failed (must contain 'task_id').
            failed_output:   The last failed output from the CorrectionLoop.
            failure_history: List of all failed attempt dicts. If not provided,
                             [failed_output] is used as a single-item history.

        Returns:
            Standard output dict from Opus dispatch, OR:
              - {"status": "escalated", ...}  — when no dispatch_fn configured
              - {"status": "error", "error": "MVFL_ESCALATION_FAILED", ...} — on exception
        """
        task_id = task_payload.get("task_id", "unknown")
        history = failure_history if failure_history is not None else [failed_output]
        attempt_count = len(history)

        # Build formatted failure history string for the Opus prompt
        history_str = self._build_history_string(history)

        # Render the escalation system prompt with real context
        system_prompt = ESCALATION_SYSTEM_PROMPT.format(
            attempt_count=attempt_count,
            failure_history=history_str,
        )

        # Log escalation event before dispatch (telemetry must never be skipped)
        self._log_event({
            "event": "mvfl_escalation",
            "task_id": task_id,
            "model": ESCALATION_MODEL,
            "attempt_count": attempt_count,
            "timestamp": datetime.now(timezone.utc).isoformat(),
        })

        # Dispatch to Opus
        try:
            if self._dispatch:
                result = await self._dispatch(ESCALATION_MODEL, system_prompt, task_payload)
                return result
            else:
                # No dispatch function — return a clear stub (used in testing / dry-run)
                return {
                    "task_id": task_id,
                    "status": "escalated",
                    "model": ESCALATION_MODEL,
                    "output": (
                        f"Escalation to {ESCALATION_MODEL} requested "
                        "(no dispatch function configured)"
                    ),
                    "attempt_count": attempt_count,
                }
        except Exception as e:
            return {
                "task_id": task_id,
                "status": "error",
                "error": "MVFL_ESCALATION_FAILED",
                "error_detail": str(e),
                "model": ESCALATION_MODEL,
            }

    def _build_history_string(self, history: list) -> str:
        """
        Format failure history list into a numbered, human-readable string.

        Each attempt is shown as a numbered block with all key-value pairs
        (or a raw string representation for non-dict entries).

        Args:
            history: List of attempt dicts or raw values.

        Returns:
            Multi-line string suitable for inclusion in the Opus system prompt.
        """
        parts = []
        for i, attempt in enumerate(history, 1):
            parts.append(f"--- Attempt {i} ---")
            if isinstance(attempt, dict):
                for k, v in attempt.items():
                    parts.append(f"  {k}: {v}")
            else:
                parts.append(f"  {attempt}")
        return "\n".join(parts)

    def _log_event(self, event: dict) -> None:
        """
        Append a JSON event line to the observability events log.
        Never raises — telemetry must not break the pipeline.
        """
        try:
            EVENTS_LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
            with open(EVENTS_LOG_PATH, "a") as f:
                f.write(json.dumps(event) + "\n")
        except Exception:
            pass


# VERIFICATION_STAMP
# Story: 3.05 (Track B) — MemGPTEscalation — Opus Escalation on 3-Strike
# Verified By: parallel-builder (claude-sonnet-4-6)
# Verified At: 2026-02-25
# Tests: 8/8
# Coverage: 100%
