"""Natrenovana siet ako hrac so standardnym rozhranim guess/play.

Rovnake rozhranie ako rl/players.py, takze funguje v rl/evaluate.py aj ako
boti "mozog" v api/bots.py. Hrac vidi len observaciu + masku z rl/encoding.py
-- z principu nemoze podvadzat (do cudzich ruk sa nedostane).
"""

import torch

from rl.encoding import encode_observation, guess_mask, play_mask
from rl.model import BridzikNet, mask_tensor, masked_categorical, obs_tensor


class NeuralPlayer:
    def __init__(self, net: BridzikNet, greedy: bool = True):
        self.net = net
        self.greedy = greedy  # argmax pri evaluacii; sampling pre pestrost

    def _act(self, rnd, seat: int, use_play_head: bool) -> int:
        obs = obs_tensor(encode_observation(rnd, seat)).unsqueeze(0)
        mask = mask_tensor(
            play_mask(rnd, seat) if use_play_head else guess_mask(rnd)
        ).unsqueeze(0)
        self.net.eval()
        with torch.no_grad():
            guess_logits, play_logits, _ = self.net(obs)
            logits = play_logits if use_play_head else guess_logits
            logits = logits.masked_fill(~mask, float('-inf'))
            if self.greedy:
                return int(logits.argmax(dim=-1).item())
            return int(masked_categorical(logits, mask).sample().item())

    def guess(self, rnd, seat: int) -> int:
        return self._act(rnd, seat, use_play_head=False)

    def play(self, rnd, seat: int) -> int:
        return self._act(rnd, seat, use_play_head=True)


def load_player(checkpoint_path: str, greedy: bool = True) -> NeuralPlayer:
    from rl.train import load_checkpoint
    return NeuralPlayer(load_checkpoint(checkpoint_path), greedy=greedy)
