"""
Loop detector — extracted from the original proxy.py POC.

Session-scoped exact-match repetition detection with configurable mitigations.
"""
from __future__ import annotations

import hashlib
import json
import logging
from collections import defaultdict, deque

log = logging.getLogger("festinger.loop")

# Key: (model, session_fingerprint)  →  deque of response hashes
_history: dict[tuple, deque] = defaultdict(lambda: deque(maxlen=20))
_consecutive: dict[tuple, int] = defaultdict(int)
_last_hash: dict[tuple, str] = {}


def session_key(model: str, messages: list[dict]) -> tuple[str, str]:
    system_msgs = [m for m in messages if m.get("role") == "system"]
    if system_msgs:
        fingerprint = hashlib.sha256(
            system_msgs[0].get("content", "")[:512].encode()
        ).hexdigest()[:16]
    else:
        non_assistant = [m for m in messages if m.get("role") != "assistant"]
        blob = json.dumps(non_assistant, sort_keys=True)
        fingerprint = hashlib.sha256(blob.encode()).hexdigest()[:16]
    return (model, fingerprint)


def hash_response(text: str) -> str:
    return hashlib.sha256(text.strip().encode()).hexdigest()


def record_and_check(session: tuple, text: str, min_length: int) -> int:
    if len(text.strip()) < min_length:
        return 1
    h = hash_response(text)
    if _last_hash.get(session) == h:
        _consecutive[session] += 1
    else:
        _consecutive[session] = 1
        _last_hash[session] = h
    _history[session].append(h)
    return _consecutive[session]


def apply_mitigations(
    request_body: dict,
    count: int,
    config: dict,
) -> tuple[dict, str | None]:
    mitigations = config.get("mitigations", [])
    override: str | None = None

    for m in mitigations:
        if not m.get("enabled", True):
            continue
        if count < m.get("trigger_count", 2):
            continue

        strategy = m["strategy"]

        if strategy == "temperature_boost":
            opts = request_body.setdefault("options", {})
            current = opts.get("temperature", 0.7)
            boosted = min(current + m.get("boost_amount", 0.35), m.get("max_temperature", 1.4))
            opts["temperature"] = boosted
            log.warning("mitigation=temperature_boost  %.2f → %.2f  (count=%d)", current, boosted, count)

        elif strategy == "forbidden_action":
            msg = m.get("injection_message", "STOP. Try something completely different.").format(count=count)
            request_body.setdefault("messages", []).append({"role": "user", "content": msg})
            log.warning("mitigation=forbidden_action  injected (count=%d)", count)

        elif strategy == "history_truncation":
            messages = request_body.get("messages", [])
            truncate = m.get("truncate_turns", 6)
            system_msgs = [m_ for m_ in messages if m_.get("role") == "system"]
            non_system = [m_ for m_ in messages if m_.get("role") != "system"]
            trimmed = non_system[:-truncate] if len(non_system) > truncate else non_system[-2:]
            request_body["messages"] = system_msgs + trimmed
            log.warning("mitigation=history_truncation  dropped %d turns (count=%d)", truncate, count)

        elif strategy == "circuit_breaker":
            override = m.get("response_message", "[LOOP DETECTOR] Halted due to repeated responses.")
            log.error("mitigation=circuit_breaker  TRIGGERED (count=%d)", count)
            break

    return request_body, override
