"""publishing/consensus_pipeline.py — 합의도출 전용 파이프라인.

3대 엔진 합의도출 워크플로우를 실행한다.
MAX_CONSENSUS_ROUNDS = 3 (하드캡).

출처: memory/specs/three-engine-consensus.md
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field

from engine_v2.engine_result import EngineResult, EngineRole

logger = logging.getLogger(__name__)

MAX_CONSENSUS_ROUNDS = 3  # 하드캡 — 무한루프 방지


@dataclass
class ConsensusState:
    """합의 상태 추적."""

    round_number: int = 0
    converged: bool = False
    consensus_score: float = 0.0  # 0.0 ~ 1.0
    minority_opinions: list[str] = field(default_factory=list)
    results_by_round: list[list[EngineResult]] = field(default_factory=list)


class ConsensusPipeline:
    """합의도출 파이프라인.

    Step 3 → Step 4 반복을 최대 MAX_CONSENSUS_ROUNDS까지 수행.
    """

    def __init__(self, threshold: float = 0.75) -> None:
        self._threshold = threshold
        self._state = ConsensusState()

    @property
    def state(self) -> ConsensusState:
        return self._state

    def evaluate_consensus(self, feedback_results: list[EngineResult]) -> float:
        """합의 점수 계산 (0.0 ~ 1.0).

        피드백의 "주요 수정 필요" 항목 수 기반.
        수정 필요 키워드가 적을수록 합의 점수가 높다.
        """
        if not feedback_results:
            return 0.0

        total_score = 0.0
        for result in feedback_results:
            if result.error:
                continue
            content = result.content.lower()
            # 수정 불필요 신호
            positive_signals = sum(
                1
                for kw in [
                    "수정 불필요",
                    "충분",
                    "적절",
                    "양호",
                    "우수",
                    "no major",
                    "acceptable",
                    "well done",
                    "good",
                ]
                if kw in content
            )
            # 수정 필요 신호
            negative_signals = sum(
                1
                for kw in [
                    "수정 필요",
                    "부족",
                    "오류",
                    "잘못",
                    "누락",
                    "major issue",
                    "incorrect",
                    "missing",
                    "needs revision",
                ]
                if kw in content
            )

            if positive_signals + negative_signals == 0:
                total_score += 0.5
            else:
                total_score += positive_signals / (positive_signals + negative_signals)

        non_error = [r for r in feedback_results if not r.error]
        if not non_error:
            return 0.0

        return total_score / len(non_error)

    def should_continue(self, feedback_results: list[EngineResult]) -> bool:
        """추가 라운드가 필요한지 판단.

        Returns:
            True면 추가 라운드 필요 (합의 미달).
        """
        self._state.round_number += 1
        score = self.evaluate_consensus(feedback_results)
        self._state.consensus_score = score
        self._state.results_by_round.append(feedback_results)

        # 소수 의견 보존
        self._preserve_minority(feedback_results)

        if score >= self._threshold:
            self._state.converged = True
            logger.info(
                "Consensus reached: score=%.2f >= threshold=%.2f",
                score,
                self._threshold,
            )
            return False

        if self._state.round_number >= MAX_CONSENSUS_ROUNDS:
            logger.warning(
                "Max rounds reached (%d), forcing convergence",
                MAX_CONSENSUS_ROUNDS,
            )
            self._state.converged = False
            return False

        logger.info(
            "Consensus not reached: score=%.2f < threshold=%.2f, round=%d",
            score,
            self._threshold,
            self._state.round_number,
        )
        return True

    def _preserve_minority(self, results: list[EngineResult]) -> None:
        """소수 의견 보존."""
        for result in results:
            if result.error:
                continue
            content = result.content.lower()
            # 반대 의견 키워드 탐지
            if any(kw in content for kw in ["반대", "동의하지 않", "우려", "concern", "disagree", "however"]):
                self._state.minority_opinions.append(
                    f"[Round {self._state.round_number}][{result.engine}] {result.content[:200]}"
                )

    def reset(self) -> None:
        """상태 초기화."""
        self._state = ConsensusState()
