"""
GetBestActionUsecase
State를 받아 최적의 협상 카드를 선택하는 Usecase
"""

from typing import Dict, Optional
from ..domain.model.state import State
from ..domain.model.q_table import QTable
from ..domain.agents.policy import UCBPolicy
from ...integration.action_card_mapper import ActionCardMapper


class GetBestActionUsecase:
    """
    협상 추론 Usecase: State → action_id → card_id

    Q_Table은 추상적인 action_id만 반환하고,
    ActionCardMapper를 통해 구체적인 card_id로 변환한다.
    """

    def __init__(
        self,
        q_table: QTable,
        policy: UCBPolicy,
        action_card_mapper: ActionCardMapper,
    ):
        """
        Args:
            q_table: 학습된 Q-Table
            policy: 액션 선택 정책 (UCB)
            action_card_mapper: action_id ↔ card_id 매핑
        """
        self.q_table = q_table
        self.policy = policy
        self.action_card_mapper = action_card_mapper

    def execute(self, state: State, use_policy: bool = True) -> Dict:
        """
        현재 상태에서 최적의 협상 카드 선택

        Args:
            state: 현재 협상 상태
            use_policy: True면 Policy 사용 (UCB + 중복 방지)
                       False면 순수 Q-value 최대값만 사용

        Returns:
            {
                'action_id': 5,
                'card_id': 'no_5',
                'q_value': 0.85,
                'state_index': 12,
                'all_q_values': [0.1, 0.2, ..., 0.85, ...]  # optional
            }

            사용 가능한 액션이 없으면:
            {
                'action_id': None,
                'card_id': None,
                'q_value': None,
                'message': 'No available actions'
            }
        """
        # 1. State → state_index 변환
        state_index = state.to_index()

        # 2. Q-values 조회
        q_values = self.q_table.get_q_values_for_state(state_index)

        # 3. Action 선택
        if use_policy:
            action_id = self.policy.select_action(state_index, q_values)
        else:
            # 순수 Q-value 최대값
            action_id = self.q_table.get_best_action(state_index)

        if action_id is None:
            return {
                "action_id": None,
                "card_id": None,
                "q_value": None,
                "state_index": state_index,
                "message": "No available actions (all actions used in this episode)",
            }

        # 4. action_id → card_id 매핑
        card_id = self.action_card_mapper.get_card_id(action_id)

        if card_id is None:
            return {
                "action_id": action_id,
                "card_id": None,
                "q_value": float(q_values[action_id]),
                "state_index": state_index,
                "message": f"action_id {action_id} has no card mapping",
            }

        # 5. Q-value 추출
        q_value = self.q_table.get_q_value(state_index, action_id)

        return {
            "action_id": action_id,
            "card_id": card_id,
            "q_value": float(q_value),
            "state_index": state_index,
            "all_q_values": q_values.tolist(),  # 디버깅용
        }

    def get_top_k_actions(self, state: State, k: int = 3) -> list:
        """
        상위 k개의 액션 추천

        Args:
            state: 현재 협상 상태
            k: 추천할 액션 개수

        Returns:
            [
                {'action_id': 5, 'card_id': 'no_5', 'q_value': 0.85},
                {'action_id': 3, 'card_id': 'no_3', 'q_value': 0.72},
                {'action_id': 1, 'card_id': 'no_1', 'q_value': 0.68}
            ]
        """
        state_index = state.to_index()
        q_values = self.q_table.get_q_values_for_state(state_index)

        # Q-value 내림차순 정렬
        sorted_actions = sorted(enumerate(q_values), key=lambda x: x[1], reverse=True)

        # 상위 k개 추출
        top_k = sorted_actions[:k]

        results = []
        for action_id, q_value in top_k:
            card_id = self.action_card_mapper.get_card_id(action_id)
            results.append(
                {"action_id": action_id, "card_id": card_id, "q_value": float(q_value)}
            )

        return results

    def reset_episode(self):
        """
        에피소드 종료 시 Policy 초기화
        (다음 협상 세션 시작 전 호출)
        """
        self.policy.reset_episode()

    def get_available_actions(self) -> list:
        """
        현재 에피소드에서 아직 사용하지 않은 액션들

        Returns:
            [0, 2, 4, 5, ...] (사용하지 않은 action_id들)
        """
        import numpy as np

        mask = self.policy.get_action_mask()
        available_action_ids = np.where(mask > 0)[0].tolist()
        return available_action_ids

    def get_available_cards(self) -> list:
        """
        현재 에피소드에서 아직 사용하지 않은 카드들

        Returns:
            [
                {'action_id': 0, 'card_id': 'no_0'},
                {'action_id': 2, 'card_id': 'no_2'},
                ...
            ]
        """
        available_action_ids = self.get_available_actions()

        results = []
        for action_id in available_action_ids:
            card_id = self.action_card_mapper.get_card_id(action_id)
            results.append({"action_id": action_id, "card_id": card_id})

        return results
