"""The Physicist — classifies constraints as physics or habit.
PCT Components 05-08: Raw Materials, Assembly, Instructions, Intolerance."""

import json
from typing import Any

from xyglue.thinking.models import BenchmarkMap, ClassifiedMap, Constraint


class Physicist:
    """Tags each benchmark element: physics (can't change) or habit (conventional, breakable)."""

    def __init__(
        self,
        name: str = "Physicist",
        ai_client: Any = None,
        model: str = "claude-sonnet-4-20250514",
    ) -> None:
        self.name = name
        self.ai_client = ai_client
        self.model = model

    async def classify(
        self, benchmark: BenchmarkMap, available_tools: list[str]
    ) -> ClassifiedMap:
        if self.ai_client is None:
            return self._rule_based_classification(benchmark)

        elements_str = json.dumps(
            [e.to_dict() for e in benchmark.elements], indent=2
        )
        prompt = f"""You are {self.name}, a constraint classification agent.

Classify each element as PHYSICS (cannot change — auth required, rate limits, TCP handshake)
or HABIT (conventional, could be different — popular library, standard pattern, REST vs GraphQL).

Be aggressive about identifying habits. Most things developers assume are necessary are just conventional.

GOAL: {benchmark.goal_text}
DOMAIN: {benchmark.domain}
AVAILABLE TOOLS: {', '.join(available_tools)}
ELEMENTS: {elements_str}

Respond with JSON:
{{"constraints": [{{"element_name": "name", "constraint_type": "physics|habit",
   "reasoning": "why", "breakable": true/false,
   "alternatives": ["alt1"] }}] }}

Respond ONLY with JSON."""

        response = await self.ai_client.messages.create(
            model=self.model,
            max_tokens=2048,
            messages=[{"role": "user", "content": prompt}],
        )
        return self._parse_response(response.content[0].text, benchmark)

    def _parse_response(self, response: str, benchmark: BenchmarkMap) -> ClassifiedMap:
        try:
            text = response.strip()
            for prefix in ["```json", "```"]:
                if text.startswith(prefix):
                    text = text[len(prefix) :]
            if text.endswith("```"):
                text = text[:-3]
            data = json.loads(text.strip())
            constraints = [
                Constraint.from_dict(c) for c in data.get("constraints", [])
            ]
            return ClassifiedMap(benchmark=benchmark, constraints=constraints)
        except (json.JSONDecodeError, KeyError):
            return self._rule_based_classification(benchmark)

    def _rule_based_classification(self, benchmark: BenchmarkMap) -> ClassifiedMap:
        constraints = []
        for element in benchmark.elements:
            name_lower = element.name.lower()
            if any(
                w in name_lower
                for w in [
                    "auth", "login", "security", "rate limit",
                    "network", "dns", "tcp", "ssl",
                ]
            ):
                constraints.append(
                    Constraint(
                        element_name=element.name,
                        constraint_type="physics",
                        reasoning="Required by infrastructure",
                        breakable=False,
                        alternatives=[],
                    )
                )
            else:
                constraints.append(
                    Constraint(
                        element_name=element.name,
                        constraint_type="habit",
                        reasoning="Convention, alternatives exist",
                        breakable=True,
                        alternatives=[f"Alternative to {element.name}"],
                    )
                )
        return ClassifiedMap(benchmark=benchmark, constraints=constraints)
