"""
Agent Replay Fixture Builder
============================

Builds sanitized incident fixtures for OpenClaw replacement candidate replay.

Fixtures separate the input context shown to candidate Agents from evaluation
labels used by the offline scoring harness. This prevents candidates from
self-grading against the answer key while keeping replay runs reproducible.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

REDACTED = "[REDACTED]"
SENSITIVE_KEY_MARKERS = (
    "authorization",
    "cookie",
    "password",
    "passwd",
    "secret",
    "token",
    "api_key",
    "apikey",
    "private_key",
)
SENSITIVE_VALUE_MARKERS = (
    "bearer ",
    "basic ",
    "-----begin private key-----",
)


@dataclass(frozen=True)
class AgentReplayFixture:
    """One sanitized incident fixture for candidate Agent offline replay."""

    run_id: str
    incident_id: str
    schema_version: str = "agent_replay_fixture_v1"
    incident_context: dict[str, Any] = field(default_factory=dict)
    evaluation_labels: dict[str, Any] = field(default_factory=dict)
    source_metadata: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        return {
            "schema_version": self.schema_version,
            "run_id": self.run_id,
            "incident_id": self.incident_id,
            "incident_context": dict(self.incident_context),
            "evaluation_labels": dict(self.evaluation_labels),
            "source_metadata": dict(self.source_metadata),
        }


def build_agent_replay_fixture(
    *,
    run_id: str,
    incident,
    evidence=None,
    execution=None,
    agent_turn_count: int = 0,
) -> AgentReplayFixture:
    """Build a sanitized fixture from DB model objects."""
    incident_context = {
        "severity": _scalar_value(getattr(incident, "severity", None)),
        "status": _scalar_value(getattr(incident, "status", None)),
        "alertname": getattr(incident, "alertname", None),
        "alert_category": getattr(incident, "alert_category", None),
        "notification_type": getattr(incident, "notification_type", None),
        "affected_services": list(getattr(incident, "affected_services", None) or []),
        "signals": _sanitize_for_fixture(getattr(incident, "signals", None) or []),
        "frequency_snapshot": _sanitize_for_fixture(
            getattr(incident, "frequency_snapshot", None)
        ),
        "evidence_summary": _sanitize_for_fixture(
            getattr(evidence, "evidence_summary", None) if evidence else None
        ),
        "mcp_health": _sanitize_for_fixture(
            getattr(evidence, "mcp_health", None) if evidence else None
        ),
        "sensors_attempted": getattr(evidence, "sensors_attempted", None)
        if evidence
        else None,
        "sensors_succeeded": getattr(evidence, "sensors_succeeded", None)
        if evidence
        else None,
        "historical_context": _sanitize_for_fixture(
            getattr(evidence, "historical_context", None) if evidence else None
        ),
        "dependency_topology": _sanitize_for_fixture(
            getattr(evidence, "dependency_topology", None) if evidence else None
        ),
        "business_metrics": _sanitize_for_fixture(
            getattr(evidence, "business_metrics", None) if evidence else None
        ),
    }
    expected_action_markers = _expected_action_markers(
        incident_context=incident_context,
        execution=execution,
    )
    evaluation_labels = {
        "verification_result": getattr(evidence, "verification_result", None)
        if evidence
        else None,
        "self_healing_score": getattr(evidence, "self_healing_score", None)
        if evidence
        else None,
        "execution_success": getattr(execution, "success", None) if execution else None,
        "execution_error": _sanitize_for_fixture(
            getattr(execution, "error_message", None) if execution else None
        ),
        "resolved_at": _iso_or_none(getattr(incident, "resolved_at", None)),
        "closed_at": _iso_or_none(getattr(incident, "closed_at", None)),
    }
    if expected_action_markers:
        evaluation_labels["expected_action_markers"] = expected_action_markers
    source_metadata = {
        "created_at": _iso_or_none(getattr(incident, "created_at", None)),
        "updated_at": _iso_or_none(getattr(incident, "updated_at", None)),
        "agent_turn_count": agent_turn_count,
        "source": "awoooi_incident_replay_fixture",
    }

    return AgentReplayFixture(
        run_id=run_id,
        incident_id=str(incident.incident_id),
        incident_context=_drop_none(incident_context),
        evaluation_labels=_drop_none(evaluation_labels),
        source_metadata=_drop_none(source_metadata),
    )


def _sanitize_for_fixture(value: Any) -> Any:
    if isinstance(value, dict):
        sanitized: dict[str, Any] = {}
        for key, nested in value.items():
            key_text = str(key)
            if _is_sensitive_key(key_text):
                sanitized[key_text] = REDACTED
            else:
                sanitized[key_text] = _sanitize_for_fixture(nested)
        return sanitized
    if isinstance(value, list):
        return [_sanitize_for_fixture(item) for item in value]
    if isinstance(value, tuple):
        return [_sanitize_for_fixture(item) for item in value]
    if isinstance(value, str):
        return _sanitize_string(value)
    if isinstance(value, datetime):
        return value.isoformat()
    return value


def _sanitize_string(value: str) -> str:
    lowered = value.lower()
    if any(marker in lowered for marker in SENSITIVE_VALUE_MARKERS):
        return REDACTED
    return value


def _is_sensitive_key(key: str) -> bool:
    lowered = key.lower()
    return any(marker in lowered for marker in SENSITIVE_KEY_MARKERS)


def _drop_none(payload: dict[str, Any]) -> dict[str, Any]:
    return {key: value for key, value in payload.items() if value is not None}


def _iso_or_none(value: Any) -> str | None:
    if value is None:
        return None
    if isinstance(value, datetime):
        return value.isoformat()
    return str(value)


def _scalar_value(value: Any) -> Any:
    return getattr(value, "value", value)


def _expected_action_markers(
    *,
    incident_context: dict[str, Any],
    execution: Any,
) -> list[str]:
    if execution is None:
        return []
    parts = [
        getattr(execution, "playbook_name", None),
        _sanitize_for_fixture(getattr(execution, "executed_steps", None) or []),
    ]
    haystack = " ".join(
        json_part.lower()
        for json_part in (_json_text(part) for part in parts)
        if json_part
    )
    markers: list[str] = []
    if "rollout restart" in haystack or ("rollout" in haystack and "restart" in haystack):
        markers.append("rollout restart")
    else:
        for marker in ("restart", "rollback", "scale", "describe", "logs", "delete"):
            if marker in haystack:
                markers.append(marker)

    for service in incident_context.get("affected_services") or []:
        service_marker = str(service).strip().lower()
        if service_marker:
            markers.append(service_marker)
            break

    return list(dict.fromkeys(markers))


def _json_text(value: Any) -> str:
    if value is None:
        return ""
    if isinstance(value, str):
        return value
    return str(value)
