@Override
  public Result<PlayerTradeInCardsResponseDeniedEvent.Reason> requestTradeInCards(
      final Id playerId, final Match tradeInCards, final TurnPhase turnPhase) {
    Arguments.checkIsNotNull(playerId, "playerId");
    Arguments.checkIsNotNull(tradeInCards, "tradeInCards");
    Arguments.checkIsNotNull(turnPhase, "turnPhase");

    final int numCards = playerCardHandler.countCardsInHand(playerId);
    final boolean isOptionalTradeInNotAllowed =
        turnPhase != TurnPhase.REINFORCE
            && numCards < rules.getMinCardsInHandForTradeInReinforcePhase();
    final boolean isRequiredTradeInNotAllowed =
        numCards >= rules.getMinCardsInHandToRequireTradeIn(turnPhase);
    if (isOptionalTradeInNotAllowed && isRequiredTradeInNotAllowed) {
      return Result.failure(Reason.TRADE_IN_NOT_ALLOWED);
    }

    final CardSet playerHand = playerCardHandler.getCardsInHand(playerId);
    final CardSet matchSet = tradeInCards.getCardSet();
    if (turnPhase == TurnPhase.REINFORCE
        && numCards - matchSet.size() > rules.getMaxCardsInHand(TurnPhase.REINFORCE)) {
      return Result.failure(Reason.TOO_MANY_CARDS_IN_HAND);
    }
    if (!playerHand.containsAll(matchSet)) return Result.failure(Reason.CARDS_NOT_IN_HAND);
    playerCardHandler.removeCardsFromHand(playerId, matchSet);
    cardDealer.discard(matchSet);
    tradeInCount++;
    return Result.success();
  }
  @Override
  public Card giveCard(final Id playerId, final TurnPhase turnPhase) {
    Arguments.checkIsNotNull(playerId, "playerId");
    Arguments.checkIsNotNull(turnPhase, "turnPhase");
    final int maxCardsInHand = rules.getMaxCardsInHand(turnPhase);
    Preconditions.checkIsTrue(
        playerCardHandler.countCardsInHand(playerId) < maxCardsInHand,
        Strings.format(
            "Player [{}] has reached maximum cards in hand [{}] for [{}]",
            playerId,
            maxCardsInHand,
            turnPhase));

    final Card card = cardDealer.take();
    playerCardHandler.addCardToHand(playerId, card);
    return card;
  }
  @Override
  public boolean existsCardWith(final String name) {
    Arguments.checkIsNotNull(name, "name");

    return cardDealer.existsCardWith(name);
  }
  @Override
  public Card cardWith(final String name) {
    Arguments.checkIsNotNull(name, "name");

    return cardDealer.cardWith(name);
  }