/*
 * Copyright (C) 2018 Lightbend Inc. <https://www.lightbend.com>
 */

package jdocs.akka.persistence.typed.auction;

import akka.Done;
import akka.actor.typed.ActorRef;
import akka.persistence.typed.ExpectingReply;

import java.util.UUID;

/**
 * An auction command.
 */
public interface AuctionCommand {

  /**
   * Start the auction.
   */
  final class StartAuction implements AuctionCommand, ExpectingReply<Done> {

    /**
     * The auction to start.
     */
    private final Auction auction;

    private final ActorRef<Done> replyTo;

    public StartAuction(Auction auction, ActorRef<Done> replyTo) {
      this.auction = auction;
      this.replyTo = replyTo;
    }

    @Override
    public ActorRef<Done> replyTo() {
      return replyTo;
    }

    public Auction getAuction() {
      return auction;
    }
  }

  /**
   * Cancel the auction.
   */
  final class CancelAuction implements AuctionCommand, ExpectingReply<Done> {
    private final ActorRef<Done> replyTo;

    public CancelAuction(ActorRef<Done> replyTo) {
      this.replyTo = replyTo;
    }

    @Override
    public ActorRef<Done> replyTo() {
      return replyTo;
    }

  }

  /**
   * Place a bid on the auction.
   */
  final class PlaceBid implements AuctionCommand, ExpectingReply<PlaceBidReply> {

    private final int bidPrice;
    private final UUID bidder;

    private final ActorRef<PlaceBidReply> replyTo;

    public PlaceBid(int bidPrice, UUID bidder, ActorRef<PlaceBidReply> replyTo) {
      this.bidPrice = bidPrice;
      this.bidder = bidder;
      this.replyTo = replyTo;
    }

    @Override
    public ActorRef<PlaceBidReply> replyTo() {
      return replyTo;
    }

    public int getBidPrice() {
      return bidPrice;
    }

    public UUID getBidder() {
      return bidder;
    }
  }

  interface PlaceBidReply {}

  /**
   * The status of placing a bid.
   */
  enum PlaceBidStatus {
    /**
     * The bid was accepted, and is the current highest bid.
     */
    ACCEPTED(BidResultStatus.ACCEPTED),
    /**
     * The bid was accepted, but was outbidded by the maximum bid of the current highest bidder.
     */
    ACCEPTED_OUTBID(BidResultStatus.ACCEPTED_OUTBID),
    /**
     * The bid was accepted, but is below the reserve.
     */
    ACCEPTED_BELOW_RESERVE(BidResultStatus.ACCEPTED_BELOW_RESERVE),
    /**
     * The bid was not at least the current bid plus the increment.
     */
    TOO_LOW(BidResultStatus.TOO_LOW),
    /**
     * The auction hasn't started.
     */
    NOT_STARTED(BidResultStatus.NOT_STARTED),
    /**
     * The auction has already finished.
     */
    FINISHED(BidResultStatus.FINISHED),
    /**
     * The auction has been cancelled.
     */
    CANCELLED(BidResultStatus.CANCELLED);

    public final BidResultStatus bidResultStatus;

    PlaceBidStatus(BidResultStatus bidResultStatus) {
      this.bidResultStatus = bidResultStatus;
    }

    public static PlaceBidStatus from(BidResultStatus status) {
      switch (status) {
        case ACCEPTED:
          return ACCEPTED;
        case ACCEPTED_BELOW_RESERVE:
          return ACCEPTED_BELOW_RESERVE;
        case ACCEPTED_OUTBID:
          return ACCEPTED_OUTBID;
        case CANCELLED:
          return CANCELLED;
        case FINISHED:
          return FINISHED;
        case NOT_STARTED:
          return NOT_STARTED;
        case TOO_LOW:
          return TOO_LOW;
        default:
          throw new IllegalStateException();
      }
    }
  }

  /**
   * The result of placing a bid.
   */
  final class PlaceBidResult implements PlaceBidReply {

    /**
     * The current price of the auction.
     */
    private final int currentPrice;
    /**
     * The status of the attempt to place a bid.
     */
    private final PlaceBidStatus status;
    /**
     * The current winning bidder.
     */
    private final UUID currentBidder;

    public PlaceBidResult(PlaceBidStatus status, int currentPrice, UUID currentBidder) {
      this.currentPrice = currentPrice;
      this.status = status;
      this.currentBidder = currentBidder;
    }

    public int getCurrentPrice() {
      return currentPrice;
    }

    public PlaceBidStatus getStatus() {
      return status;
    }

    public UUID getCurrentBidder() {
      return currentBidder;
    }
  }

  final class PlaceBidRejected implements PlaceBidReply {
    private final String errorMessage;

    public PlaceBidRejected(String errorMessage) {
      this.errorMessage = errorMessage;
    }

    public String getErrorMessage() {
      return errorMessage;
    }
  }

  /**
   * Finish bidding.
   */
  final class FinishBidding implements AuctionCommand, ExpectingReply<Done> {

    private final ActorRef<Done> replyTo;

    FinishBidding(ActorRef<Done> replyTo) {
      this.replyTo = replyTo;
    }

    @Override
    public ActorRef<Done> replyTo() {
      return replyTo;
    }
  }

  /**
   * Get the auction.
   */
  final class GetAuction implements AuctionCommand, ExpectingReply<AuctionState> {
    private final ActorRef<AuctionState> replyTo;

    public GetAuction(ActorRef<AuctionState> replyTo) {
      this.replyTo = replyTo;
    }

    @Override
    public ActorRef<AuctionState> replyTo() {
      return replyTo;
    }
  }
}
