"""Blind pairwise comparison between conditions."""

from __future__ import annotations

import random
import re
from dataclasses import dataclass, field
from typing import Dict, List, Tuple

from .llm_client import JudgeLLM
from .prompts import PAIRWISE_SYSTEM_PROMPT, PAIRWISE_USER_PROMPT


@dataclass
class PairwiseResult:
    text_id: str
    condition_a: str  # actual condition of "A" slot
    condition_b: str  # actual condition of "B" slot
    winner: str  # "A", "B", or "TIE" (as judged)
    true_winner: str  # condition ID of actual winner (de-blinded)
    reasoning: str = ""
    per_criterion: Dict[str, str] = field(default_factory=dict)


def _parse_pairwise(response: str) -> Tuple[str, str, Dict[str, str]]:
    """Parse winner label, reasoning, and per-criterion votes from response."""
    # Winner — forced choice; TIE should not appear but handle gracefully
    m = re.search(r"Winner:\s*(A|B|TIE)", response, re.IGNORECASE)
    winner = m.group(1).upper() if m else "TIE"
    if winner == "TIE":
        # Judge violated forced-choice instruction — break tie randomly
        import logging
        logging.getLogger(__name__).warning(
            "Judge returned TIE despite forced-choice prompt; breaking randomly"
        )
        winner = random.choice(["A", "B"])

    # Reasoning
    m = re.search(r"Reasoning:\s*(.+?)(?:\n|Per-criterion)", response, re.DOTALL)
    reasoning = m.group(1).strip() if m else ""

    # Per-criterion
    criteria = {}
    for crit in ["Structural depth", "Multi-perspective", "Integration",
                 "Insight quality", "Error absence"]:
        m = re.search(rf"{crit}:\s*(A|B|TIE)", response, re.IGNORECASE)
        if m:
            criteria[crit] = m.group(1).upper()
    return winner, reasoning, criteria


async def compare_pair(
    judge: JudgeLLM,
    text_id: str,
    input_text: str,
    output_a: str,
    condition_a: str,
    output_b: str,
    condition_b: str,
) -> PairwiseResult:
    """Run one pairwise comparison with randomised presentation order."""
    # Anti-bias: randomise which output is shown as "A" vs "B"
    if random.random() < 0.5:
        shown_a, shown_b = output_a, output_b
        map_a, map_b = condition_a, condition_b
    else:
        shown_a, shown_b = output_b, output_a
        map_a, map_b = condition_b, condition_a

    response = await judge.judge(
        PAIRWISE_SYSTEM_PROMPT,
        PAIRWISE_USER_PROMPT.format(
            input_text=input_text[:2000],
            analysis_a=shown_a[:4000],
            analysis_b=shown_b[:4000],
        ),
    )

    winner_label, reasoning, per_criterion = _parse_pairwise(response)

    if winner_label == "A":
        true_winner = map_a
    elif winner_label == "B":
        true_winner = map_b
    else:
        true_winner = "TIE"

    return PairwiseResult(
        text_id=text_id,
        condition_a=map_a,
        condition_b=map_b,
        winner=winner_label,
        true_winner=true_winner,
        reasoning=reasoning,
        per_criterion=per_criterion,
    )


async def run_all_pairwise(
    judge: JudgeLLM,
    text_id: str,
    input_text: str,
    outputs: Dict[str, str],
) -> List[PairwiseResult]:
    """Run all C(n,2) pairwise comparisons for one text."""
    conditions = list(outputs.keys())
    results: List[PairwiseResult] = []
    for i in range(len(conditions)):
        for j in range(i + 1, len(conditions)):
            result = await compare_pair(
                judge,
                text_id,
                input_text,
                outputs[conditions[i]],
                conditions[i],
                outputs[conditions[j]],
                conditions[j],
            )
            results.append(result)
    return results
