"""토론 매니저: 봇 간 라운드 로빈 토론 모드를 관리합니다.

여러 봇(잼민이/코덱스/클로디)이 순서를 정해 토론할 수 있도록
턴 관리, 시간 제한, idle 타임아웃을 처리합니다.
"""

from __future__ import annotations

import re
import time
from dataclasses import dataclass, field
from enum import Enum

# ---------------------------------------------------------------------------
# 코드 분석 감지
# ---------------------------------------------------------------------------

CODE_ANALYSIS_KEYWORDS = [
    "코드",
    "파일",
    "소스",
    "함수",
    "클래스",
    "컴포넌트",
    "구현",
    "로직",
    "아키텍처",
    "구조",
    "읽어",
    "분석해",
    "소스코드",
    "디렉토리",
    "모듈",
    "라이브러리",
    "패키지",
    "import",
    "설정파일",
    "config",
    "code",
    "file",
    "source",
    "function",
    "class",
    "architecture",
    "structure",
    "analyze",
    "implementation",
    "directory",
]


def detect_code_analysis(message_text: str) -> bool:
    """유저 메시지에서 코드 분석 필요 여부를 감지."""
    text_lower = message_text.lower()
    keyword_count = sum(1 for kw in CODE_ANALYSIS_KEYWORDS if kw in text_lower)
    has_path = bool(re.search(r"(/home/|\.py|\.js|\.ts|\.tsx|\.json|\.md)", text_lower))
    return keyword_count >= 2 or has_path


# ---------------------------------------------------------------------------
# 시간 파싱
# ---------------------------------------------------------------------------


def parse_duration(text: str) -> int | None:
    """유저 메시지에서 시간 키워드를 파싱하여 분 단위로 반환.

    지원 패턴:
    - "N분간", "N분동안", "N분"
    - "N시간동안", "N시간"
    - "Nmin", "Nhour", "Nhr"

    최대 180분(3시간). 없으면 None 반환.
    """
    MAX_MINUTES = 180

    # 한국어: N시간 (동안) — 분보다 먼저 체크해야 "1시간30분" 같은 케이스 대응
    match = re.search(r"(\d+)\s*시간", text)
    if match:
        hours = int(match.group(1))
        minutes = hours * 60
        return min(minutes, MAX_MINUTES)

    # 한국어: N분 (간/동안/bare)
    match = re.search(r"(\d+)\s*분", text)
    if match:
        minutes = int(match.group(1))
        return min(minutes, MAX_MINUTES)

    # 영어: Nhour / Nhours
    match = re.search(r"(\d+)\s*hours?", text, re.IGNORECASE)
    if match:
        hours = int(match.group(1))
        minutes = hours * 60
        return min(minutes, MAX_MINUTES)

    # 영어: Nhr
    match = re.search(r"(\d+)\s*hr", text, re.IGNORECASE)
    if match:
        hours = int(match.group(1))
        minutes = hours * 60
        return min(minutes, MAX_MINUTES)

    # 영어: Nmin / Nmins
    match = re.search(r"(\d+)\s*min", text, re.IGNORECASE)
    if match:
        minutes = int(match.group(1))
        return min(minutes, MAX_MINUTES)

    return None


# ---------------------------------------------------------------------------
# DiscussionPhase
# ---------------------------------------------------------------------------


class DiscussionPhase(str, Enum):
    DIVERGE = "diverge"  # 발산: 새로운 관점 제시
    CONVERGE = "converge"  # 수렴: 공통점 정리, 이견 좁히기
    CONSENSUS = "consensus"  # 합의: 합의문 작성


# ---------------------------------------------------------------------------
# DiscussionState
# ---------------------------------------------------------------------------


@dataclass
class DiscussionState:
    """chat_id별 토론 상태."""

    active: bool = False
    start_time: float = 0.0
    duration_minutes: int | None = None  # None = 5분 idle 규칙 적용
    last_user_message_time: float = 0.0
    current_turn: str = ""  # 현재 턴인 봇 username
    chat_id: int = 0
    code_analysis_mode: bool = False  # 코드 분석 모드 활성 여부
    round_count: int = 0  # 완료된 라운드 수 (3봇 응답 = 1라운드)
    bot_response_count: int = 0  # 현재 라운드 내 봇 응답 수
    codex_model: str = "gpt-5.1-codex-mini"  # 코덱스 모델 (--deep 시 변경)
    deep_announced: bool = False  # 심층 분석 모드 안내 완료 여부


# ---------------------------------------------------------------------------
# DiscussionManager
# ---------------------------------------------------------------------------


class DiscussionManager:
    """봇 간 라운드 로빈 토론 모드를 관리하는 클래스.

    - 유저 메시지 시 토론 모드 진입
    - 봇 응답 후 라운드 로빈으로 다음 턴 설정
    - 5분 idle 또는 지정 시간 초과 시 토론 종료
    """

    BOT_USERNAMES: list[str] = ["gemini_view_bot", "codex_view_bot", "claude_view_bot"]
    IDLE_TIMEOUT: int = 900  # 15분 (초)
    MAX_DURATION: int = 180  # 최대 3시간 (분)
    TURN_DELAY: int = 4  # 턴 간 딜레이 (초)

    def __init__(self) -> None:
        # chat_id → DiscussionState
        self._states: dict[int, DiscussionState] = {}
        # 현재 활성 chat_id (단일 채팅 전제)
        self._active_chat_id: int = 0
        self._start_index: int = 0  # 라운드 로빈 시작점 회전용
        self._chain_running: dict[int, bool] = {}

    # ------------------------------------------------------------------
    # 내부 헬퍼
    # ------------------------------------------------------------------

    def _get_state(self, chat_id: int) -> DiscussionState:
        """chat_id에 해당하는 상태를 반환. 없으면 신규 생성."""
        if chat_id not in self._states:
            self._states[chat_id] = DiscussionState(chat_id=chat_id)
        return self._states[chat_id]

    def _active_state(self) -> DiscussionState | None:
        """현재 활성 채팅의 상태를 반환. 없으면 None."""
        if self._active_chat_id == 0:
            return None
        return self._states.get(self._active_chat_id)

    # ------------------------------------------------------------------
    # 공개 메서드
    # ------------------------------------------------------------------

    def is_chain_running(self, chat_id: int) -> bool:
        """해당 채팅에서 봇 응답 체인이 실행 중인지 반환."""
        return self._chain_running.get(chat_id, False)

    def set_chain_running(self, chat_id: int, running: bool) -> None:
        """봇 응답 체인 실행 상태를 설정."""
        self._chain_running[chat_id] = running

    def is_discussion_active(self, chat_id: int) -> bool:
        """해당 채팅에서 토론 모드가 활성화되어 있는지 반환."""
        state = self._states.get(chat_id)
        if state is None:
            return False
        return state.active

    def is_code_analysis_mode(self, chat_id: int) -> bool:
        """해당 채팅이 코드 분석 모드인지 반환."""
        state = self._states.get(chat_id)
        return state.code_analysis_mode if state else False

    def get_codex_model(self, chat_id: int) -> str:
        """해당 채팅의 코덱스 모델을 반환."""
        state = self._states.get(chat_id)
        return state.codex_model if state else "gpt-5.1-codex-mini"

    def should_announce_deep_mode(self, chat_id: int) -> bool:
        """deep 모드 안내가 필요하면 True 반환 (1회만)."""
        state = self._states.get(chat_id)
        if state and state.codex_model == "gpt-5.2-codex" and not state.deep_announced:
            state.deep_announced = True
            return True
        return False

    def is_my_turn(self, bot_username: str, chat_id: int) -> bool:
        """해당 채팅에서 현재 이 봇의 턴인지 확인."""
        state = self._states.get(chat_id)
        if state is None or not state.active:
            return False
        return state.current_turn.lower() == bot_username.lower()

    def on_user_message(self, chat_id: int, message_text: str) -> bool:
        """유저 메시지 수신 시 호출. 새 토론 시작이면 True, 기존 토론 중이면 False 반환.

        - 시간 키워드 파싱
        - last_user_message_time 갱신
        - 토론 모드 활성화
        """
        state = self._get_state(chat_id)
        now = time.time()

        # --deep 감지 및 제거
        cleaned_text = message_text
        if "--deep" in message_text:
            cleaned_text = message_text.replace("--deep", "").strip()

        duration = parse_duration(cleaned_text)

        is_new = False
        if not state.active:
            # 신규 토론 시작
            is_new = True
            state.active = True
            state.start_time = now
            state.duration_minutes = duration
            if "--deep" in message_text:
                state.codex_model = "gpt-5.2-codex"
            else:
                state.codex_model = "gpt-5.1-codex-mini"
            if detect_code_analysis(cleaned_text):
                state.code_analysis_mode = True
                state.current_turn = "claude_view_bot"
            else:
                state.code_analysis_mode = False
                state.current_turn = self.BOT_USERNAMES[self._start_index % len(self.BOT_USERNAMES)]
            self._start_index += 1
            self._active_chat_id = chat_id
        else:
            # 토론 진행 중 유저 메시지 — idle 타이머만 리셋
            # 시간 한정 모드로 전환 요청이 있으면 갱신
            if duration is not None:
                state.duration_minutes = duration

        state.last_user_message_time = now
        return is_new

    def on_bot_response(self, bot_username: str) -> str | None:
        """봇이 응답한 후 호출.

        라운드 로빈으로 다음 턴을 설정하고 다음 봇 username을 반환.
        토론이 종료되어야 하면 None 반환.
        """
        state = self._active_state()
        if state is None or not state.active:
            return None

        # 라운드 카운트 업데이트
        state.bot_response_count += 1
        if state.bot_response_count >= 3:
            state.round_count += 1
            state.bot_response_count = 0

        if self.check_should_stop():
            self.stop_discussion()
            return None

        next_bot = self.get_next_bot()
        state.current_turn = next_bot
        return next_bot

    def advance_turn(self) -> str | None:
        """다음 봇으로 턴 진행.

        시간 초과/idle 체크 후:
        - 계속: 다음 봇 username 반환
        - 종료: None 반환
        """
        state = self._active_state()
        if state is None or not state.active:
            return None

        if self.check_should_stop():
            self.stop_discussion()
            return None

        next_bot = self.get_next_bot()
        state.current_turn = next_bot
        return next_bot

    def stop_discussion(self) -> None:
        """토론 중단, 활성 chat_id의 state 초기화."""
        state = self._active_state()
        if state is not None:
            state.active = False
            state.current_turn = ""
            state.duration_minutes = None
            state.start_time = 0.0
            state.last_user_message_time = 0.0
            state.code_analysis_mode = False
            state.round_count = 0
            state.bot_response_count = 0
            state.codex_model = "gpt-5.1-codex-mini"
            state.deep_announced = False
            self._chain_running[state.chat_id] = False
        self._active_chat_id = 0

    def get_next_bot(self) -> str:
        """라운드 로빈으로 다음 봇 username 반환."""
        state = self._active_state()
        if state is None or not state.current_turn:
            return self.BOT_USERNAMES[0]

        current = state.current_turn
        try:
            idx = self.BOT_USERNAMES.index(current)
        except ValueError:
            return self.BOT_USERNAMES[0]

        next_idx = (idx + 1) % len(self.BOT_USERNAMES)
        return self.BOT_USERNAMES[next_idx]

    def get_current_phase(self, chat_id: int) -> DiscussionPhase:
        """현재 토론 Phase를 계산.

        시간 지정 모드 (duration_minutes != None):
          - 0~60%: DIVERGE
          - 60~90%: CONVERGE
          - 90~100%: CONSENSUS

        idle 모드 (duration_minutes == None):
          - 라운드 1~3: DIVERGE
          - 라운드 4~6: CONVERGE
          - 라운드 7+: CONSENSUS
        """
        state = self._states.get(chat_id)
        if state is None or not state.active:
            return DiscussionPhase.DIVERGE

        if state.duration_minutes is not None:
            # 시간 지정 모드: 경과 비율로 Phase 결정
            elapsed_seconds = time.time() - state.start_time
            total_seconds = state.duration_minutes * 60
            if total_seconds <= 0:
                return DiscussionPhase.DIVERGE
            ratio = elapsed_seconds / total_seconds
            if ratio < 0.6:
                return DiscussionPhase.DIVERGE
            elif ratio < 0.9:
                return DiscussionPhase.CONVERGE
            else:
                return DiscussionPhase.CONSENSUS
        else:
            # idle 모드: round_count 기반 (완료된 라운드 수)
            # round_count 0~2 → 라운드 1~3 → DIVERGE
            # round_count 3~5 → 라운드 4~6 → CONVERGE
            # round_count 6+  → 라운드 7+  → CONSENSUS
            if state.round_count < 3:
                return DiscussionPhase.DIVERGE
            elif state.round_count < 6:
                return DiscussionPhase.CONVERGE
            else:
                return DiscussionPhase.CONSENSUS

    def check_should_stop(self) -> bool:
        """토론을 종료해야 하는지 확인.

        - 시간 한정 모드(duration_minutes 지정): 경과 시간이 duration_minutes 초과 시 True
        - idle 모드(duration_minutes=None): last_user_message_time으로부터 5분 경과 시 True
        """
        state = self._active_state()
        if state is None or not state.active:
            return True

        now = time.time()

        if state.duration_minutes is not None:
            # 시간 한정 모드: 경과 시간(분) 체크
            elapsed_minutes = (now - state.start_time) / 60
            return elapsed_minutes >= state.duration_minutes
        else:
            # idle 모드: 마지막 유저 메시지로부터 IDLE_TIMEOUT 초 체크
            idle_seconds = now - state.last_user_message_time
            return idle_seconds >= self.IDLE_TIMEOUT

    def should_bot_respond(
        self,
        bot_username: str,
        sender_username: str,
        sender_is_bot: bool,
        chat_id: int,
    ) -> bool:
        """이 봇이 해당 메시지에 토론 컨텍스트에서 응답해야 하는지 판단.

        - 토론 모드가 아닐 때: 봇 메시지에 응답 안함 (기존 동작 유지)
        - 토론 모드일 때: current_turn이 자신이면 True, 아니면 False
        - 시간/idle 체크도 수행
        """
        if not self.is_discussion_active(chat_id):
            # 토론 모드 비활성 → 봇 메시지에는 응답 안함
            if sender_is_bot:
                return False
            # 일반 유저 메시지는 별도 로직(should_respond)에서 처리
            return False

        # 토론 모드 활성
        if self.check_should_stop():
            self.stop_discussion()
            return False

        state = self._get_state(chat_id)
        return state.current_turn.lower() == bot_username.lower()
