package eu.jonahbauer.wizard.core.states.round;

import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.TransientState;
import org.jetbrains.annotations.NotNull;

import java.util.*;

import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.PICK_TRUMP;
import static eu.jonahbauer.wizard.core.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TRUMP_CARD;

public final class DeterminingTrumpUserInput extends RoundState implements TransientState {
    private final transient UUID player;
    private final transient boolean werewolf;

    public DeterminingTrumpUserInput(GameData data) {
        super(data.require(TRUMP_CARD).requireEach(PLAYERS, HANDS).require(CURRENT_PLAYER));

        this.player = get(CURRENT_PLAYER);
        this.werewolf = get(HANDS).get(this.player).contains(Card.WEREWOLF);
    }

    @Override
    public Optional<GameState> onEnter(Game game) {
        game.notify(new UserInputMessage(this.player, PICK_TRUMP, getTimeout(game, true)));
        return timeout(game);
    }

    @Override
    public Optional<GameState> onTimeout(Game game) {
        game.notify(new TimeoutMessage());

        Card.Suit[] suits;
        if (werewolf) {
            suits = new Card.Suit[] {Card.Suit.BLUE, Card.Suit.GREEN, Card.Suit.RED, Card.Suit.YELLOW, Card.Suit.NONE};
        } else {
            suits = new Card.Suit[] {Card.Suit.BLUE, Card.Suit.GREEN, Card.Suit.RED, Card.Suit.YELLOW};
        }
        return transition(game, suits[game.getRandom().nextInt(suits.length)]);
    }

    @Override
    public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
        if (this.player.equals(player) && message instanceof PickTrumpMessage trumpMessage) {
            checkTrumpSuit(trumpMessage.getTrumpSuit());
            return transition(game, trumpMessage.getTrumpSuit());
        } else {
            return super.onMessage(game, player, message);
        }
    }

    private void checkTrumpSuit(Card.Suit suit) {
        if (!werewolf && suit == Card.Suit.NONE) {
            throw new IllegalArgumentException("Trump suit must not be " + Card.Suit.NONE + ".");
        } else if (suit == null) {
            throw new IllegalArgumentException("Trump suit must not be null.");
        }
    }

    private Optional<GameState> transition(Game game, @NotNull Card.Suit trumpSuit) {
        GameData data = getData().with(
                TRUMP_SUIT, trumpSuit,
                CURRENT_PLAYER, getNextPlayer(getDealer())
        );

        if (werewolf) {
            var mutableHands = new HashMap<>(get(HANDS));
            var mutableHand = new ArrayList<>(mutableHands.get(player));
            mutableHand.set(mutableHand.indexOf(Card.WEREWOLF), get(TRUMP_CARD));
            mutableHands.put(player, List.copyOf(mutableHand));
            data = data.with(HANDS, Map.copyOf(mutableHands));
            game.notify(new TrumpMessage(Card.WEREWOLF, trumpSuit));
            game.notify(player, new HandMessage(player, mutableHands.get(player)));
        } else {
            game.notify(new TrumpMessage(get(TRUMP_CARD), trumpSuit));
        }
        return sync(data, Predicting::new);
    }
}
