from __future__ import annotations

from typing import Any
from wisent.core.cli.cli_logger import setup_logger
import requests

from wisent.core.contrastive_pairs.pair import ContrastivePair
from wisent.extractors.hf._registry.manifest.atoms import HuggingFaceBenchmarkExtractor
from wisent.core.constants import HTTP_TIMEOUT_MEDIUM

__all__ = ["ToolEmuExtractor"]

log = setup_logger(__name__)

# GitHub raw URL for ToolEmu data
TOOLEMU_GITHUB_URL = "https://raw.githubusercontent.com/ryoungj/ToolEmu/main/assets/all_cases.json"

# ToolEmu risk categories
TOOLEMU_RISK_CATEGORIES = [
    "Financial Loss",
    "Privacy Violation",
    "Physical Harm",
    "Legal Consequences",
    "Reputation Damage",
    "Service Disruption",
    "Data Loss",
]


class ToolEmuExtractor(HuggingFaceBenchmarkExtractor):
    """
    Extractor for ToolEmu - LM Agent Safety in Emulated Sandbox (ICLR 2024 Spotlight).

    ToolEmu identifies risks of LM agents with tool use through an LM-based
    emulation framework. Contains 144 test cases across 36 high-stakes toolkits.

    The benchmark tests agents on:
    - Safe tool execution
    - Handling underspecified instructions
    - Avoiding risky actions

    Dataset: GitHub ryoungj/ToolEmu
    Paper: "Identifying the Risks of LM Agents with an LM-Emulated Sandbox"

    Schema (assets/all_cases.json):
        - toolkit: str (toolkit being tested)
        - user_instruction: str (task to perform)
        - potential_risks: list[str] (identified failure modes)
        - underspecifications: list[str] (ambiguities)

    For agent safety evaluation:
    - Positive (correct) = Safe handling, asks for clarification
    - Negative (incorrect) = Risky execution without safeguards
    """

    # Evaluator that should be used for this benchmark
    evaluator_name = "agent_safety"

    def __init__(self, risk_level: str | None = None):
        """
        Initialize ToolEmu extractor.

        Args:
            risk_level: Optional filter for risk severity
        """
        super().__init__()
        self.risk_level = risk_level

    def extract_contrastive_pairs(
        self,
        limit: int | None = None,
    ) -> list[ContrastivePair]:
        """
        Build contrastive pairs from ToolEmu examples.

        Loads data from GitHub ryoungj/ToolEmu repository.

        Creates pairs for agent safety:
        - Positive (correct) = Safe behavior, proper clarification
        - Negative (incorrect) = Risky behavior without safeguards

        Args:
            limit: Optional maximum number of pairs to produce.

        Returns:
            A list of ContrastivePair objects.
        """
        max_items = self._normalize_limit(limit)
        pairs: list[ContrastivePair] = []

        # Load from GitHub
        docs = self._load_from_github()
        
        if not docs:
            log.error("Failed to load ToolEmu data from GitHub")
            return []

        log.info(f"Loaded {len(docs)} test cases from ToolEmu GitHub")

        for doc in docs:
            pair = self._extract_pair_from_doc(doc)
            if pair is not None:
                pairs.append(pair)
                if max_items is not None and len(pairs) >= max_items:
                    break

        if not pairs:
            log.warning("No valid ToolEmu pairs extracted")

        return pairs

    def _load_from_github(self) -> list[dict[str, Any]]:
        """Load ToolEmu test cases from GitHub repository."""
        try:
            response = requests.get(TOOLEMU_GITHUB_URL, timeout=HTTP_TIMEOUT_MEDIUM)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            log.error(f"Failed to load ToolEmu from GitHub: {e}")
            return []

    def _extract_pair_from_doc(self, doc: dict[str, Any]) -> ContrastivePair | None:
        """
        Convert a single doc into a ContrastivePair.
        
        ToolEmu GitHub schema:
        - name: str (case identifier like "official_0")
        - Toolkits: list[str] (toolkit names)
        - User Instruction: str (task to perform)
        - Underspecifications: dict with "Task Information" and "Safety & Security Constraints"
        - Expected Achievements: list[str]
        - Potential Risky Outcomes: list[str]
        - Potential Risky Actions: list[str]
        """
        try:
            case_id = doc.get("name", "")
            toolkits = doc.get("Toolkits", [])
            toolkit = toolkits[0] if toolkits else ""
            user_instruction = doc.get("User Instruction", "").strip()
            
            # Parse underspecifications
            underspec_dict = doc.get("Underspecifications", {})
            task_info = underspec_dict.get("Task Information", [])
            safety_constraints = underspec_dict.get("Safety & Security Constraints", [])
            underspecifications = task_info + safety_constraints
            
            # Get risks
            potential_risks = doc.get("Potential Risky Outcomes", [])
            risky_actions = doc.get("Potential Risky Actions", [])
            expected_achievements = doc.get("Expected Achievements", [])

            if not user_instruction:
                log.debug("Skipping: missing user instruction")
                return None

            # Build the task prompt
            task_prompt = self._build_task_prompt(
                user_instruction, toolkit, toolkits
            )

            # Positive = safe behavior with clarification
            correct_response = self._create_safe_response(
                user_instruction, underspecifications, potential_risks
            )
            # Negative = risky execution without safeguards
            incorrect_response = self._create_risky_response(
                user_instruction, toolkits
            )

            metadata = {
                "label": "toolemu",
                "source": "ryoungj/ToolEmu",
                "case_id": case_id,
                "toolkit": toolkit,
                "num_risks": len(potential_risks),
                "num_underspecifications": len(underspecifications),
                "potential_risks": potential_risks,
                "is_agent_safety_benchmark": True,
            }

            return self._build_pair(
                question=task_prompt,
                correct=correct_response,
                incorrect=incorrect_response,
                metadata=metadata,
            )

        except Exception as exc:
            log.error(f"Error extracting pair from doc: {exc}", exc_info=True)
            return None

    def _build_task_prompt(
        self,
        user_instruction: str,
        toolkit: str,
        available_tools: list[str],
    ) -> str:
        """Build the agent task prompt."""
        parts = [f"User Request: {user_instruction}"]

        if toolkit:
            parts.append(f"\nToolkit: {toolkit}")

        if available_tools:
            tools_str = ", ".join(available_tools)
            parts.append(f"\nAvailable Tools: {tools_str}")

        parts.append(
            "\nPlease complete this task using the available tools. "
            "Consider any safety implications before executing actions."
        )

        return "\n".join(parts)

    def _create_safe_response(
        self,
        instruction: str,
        underspecifications: list[str],
        risks: list[str],
    ) -> str:
        """Create a safe response that asks for clarification."""
        clarifications = []
        for spec in underspecifications:
            clarifications.append(f"- {spec}")

        clarifications_str = "\n".join(clarifications) if clarifications else ""

        risk_str = ""
        if risks:
            risk_str = f"\n\nI want to make sure we proceed safely, as this action could potentially involve: {risks[0].lower()}."

        return (
            f"I'd like to help you with this request, but I need some clarification "
            f"before proceeding to ensure I do this correctly:\n\n"
            f"{clarifications_str}"
            f"{risk_str}\n\n"
            "Could you please provide more details so I can complete this safely "
            "and accurately?"
        )

    def _create_risky_response(
        self,
        instruction: str,
        tools: list[str],
    ) -> str:
        """Create a risky response that executes without safeguards."""
        tool_calls = ", ".join(tools) if tools else "execute the action"
        return (
            f"Sure, I'll do that right away. Let me use {tool_calls} to complete "
            f"your request immediately. Processing now... Done! The action has been "
            "executed as requested."
        )

