package com.codingame.game;

import com.codingame.gameengine.core.MultiplayerGameManager;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Singleton
public class CommandManager {
    @Inject
    private MultiplayerGameManager<Player> gameManager;
    static final Pattern PLAYER_PLAY_PATTERN = Pattern.compile(
            "^PLAY (?<cardId>\\d+)(?:\\s+(?<message>.*))?"
    );
    static final Pattern PLAYER_PICK_PATTERN = Pattern.compile(
            "^PICK (?<lineId>\\d+)(?:\\s+(?<message>.*))?"
    );
    static final Pattern RANDOM_PATTERN = Pattern.compile(
            "^RANDOM(?:\\s+(?<message>.*))?"
    );

    public void parseCommands(Player player, List<String> lines, Random random, boolean pickPhase) {
        for (String command : lines) {
            try {
                parseCommand(player, command, random, pickPhase);
            } catch (InvalidInputException e) {
                gameManager.addToGameSummary(
                        String.format(
                                "%s provided invalid input. Expected '%s'\nGot '%s'",
                                player.getNicknameToken(),
                                e.getExpected(),
                                e.getGot()
                        )
                );
                gameManager.addToGameSummary(String.format(
                        "%s was disqualified.",
                        player.getNicknameToken()
                        )
                );
                deactivatePlayer(player, e.getMessage());
            } catch (GameRuleException e) {
                gameManager.addToGameSummary(
                        String.format(
                                "%s provided incorrect input. %s",
                                player.getNicknameToken(),
                                e.getErrorMessage()
                        )
                );
                gameManager.addToGameSummary(String.format(
                        "%s was disqualified.",
                        player.getNicknameToken()
                        )
                );
                deactivatePlayer(player, e.getMessage());
            }
        }
    }

    public void deactivatePlayer(Player player, String message) {
        player.deactivate(escapeHTMLEntities(message));
        player.setScore(-999);
    }

    private String escapeHTMLEntities(String message) {
        return message
                .replace("&lt;", "<")
                .replace("&gt;", ">");
    }

    public void parseCommand(Player player, String command, Random random, boolean pickPhase) throws GameRuleException, InvalidInputException {
        Matcher match;

        if (pickPhase) {
            match = PLAYER_PICK_PATTERN.matcher(command);
            if (match.matches()) {
                int getLine = Integer.parseInt(match.group("lineId"));
                if (getLine < 0 || getLine>=4) {
                    throw new GameRuleException(command, "you can only pick a line from 0 to 3");
                }
                player.setGetLine(getLine);
                matchMessage(player, match);
                return;
            }
        } else {
            match = PLAYER_PLAY_PATTERN.matcher(command);
            if (match.matches()) {
                int playedCard = Integer.parseInt(match.group("cardId"));
                if (player.getCards().stream().filter(c -> c.getValue()==playedCard).count() == 0) {
                    throw new GameRuleException(command, "you can only play a card that is in your hand");
                }
                player.setPlayedCard(playedCard);
                matchMessage(player, match);
                return;
            }
        }

        match = RANDOM_PATTERN.matcher(command);
        if (match.matches()) {
            if (pickPhase) {
                int lineToPick = random.nextInt(4);
                player.setGetLine(lineToPick);
            } else {
                int cardId = player.getCards().get(random.nextInt(player.getCardsCount())).getValue();
                player.setPlayedCard(cardId);
            }
            matchMessage(player, match);
            return;
        }

        throw new InvalidInputException(Game.getExpected(pickPhase), command);
    }

    private void matchMessage(Player player, Matcher match) {
        String message = match.group("message");
        if (message != null) {
            String trimmed = message.trim();
            if (trimmed.length() > 48) {
                trimmed = trimmed.substring(0, 46) + "...";
            }
            player.setMessage(trimmed);
        }
    }
}
