package org.jboss.seam.example.seambay;

import static org.jboss.seam.ScopeType.CONVERSATION;

import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;

import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Conversation;

@Scope(CONVERSATION)
@Name("bidAction")
public class BidAction
{   
   @In EntityManager entityManager;
   
   private Bid bid;
   
   @In(required = false) @Out(required = false)
   private Auction auction;
   
   @In(required = false)
   private Account authenticatedAccount;
   
   private String outcome;
   
   @Begin(join = true)
   public void placeBid()
   {     
      if (auction.getStatus() != Auction.STATUS_LIVE ||
           auction.getEndDate().getTime() < System.currentTimeMillis())
      {
         outcome = "ended";
      }
      else
      {
         bid = new Bid();
         bid.setAuction(auction);
         
         updateBid();
      }
   }
   
   public void updateBid()
   {
      String amt = Contexts.getEventContext().isSet("bidAmount") ?
            Contexts.getEventContext().get("bidAmount").toString() : null;
      
      if (amt != null)
      {
         double amount = Double.parseDouble(amt.toString());            
         if (amt != null && amount >= bid.getAuction().getRequiredBid())
         {        
            bid.setMaxAmount(amount);
            outcome = "confirm";
         }      
         else
         {
            outcome = "invalid";
         }               
      }
      else
      {
         outcome = "required";
      }
   }
   
   @SuppressWarnings("unchecked")
   public String confirmBid()
   {
      // We set the user here because the user may not be authenticated when placeBid() is called. 
      bid.setAccount(authenticatedAccount);      
      bid.setBidDate(new Date());
      
      // This is where the tricky bidding logic happens
      
      if (!entityManager.contains(bid.getAuction()))
      {
         bid.setAuction(entityManager.find(Auction.class, bid.getAuction().getAuctionId()));
      }
      
      entityManager.refresh(bid.getAuction());
      
      if (bid.getAuction().getStatus() != Auction.STATUS_LIVE)
      {
         outcome = "ended";
         return outcome;
      }
      else if (bid.getAuction().getEndDate().getTime() < bid.getBidDate().getTime())
      {
         bid.getAuction().setStatus(Auction.STATUS_COMPLETED);
         outcome = "ended";
         return outcome;
      }
            
      List<Bid> bids = entityManager.createQuery(
            "from Bid b where b.auction = :auction")
          .setParameter("auction", bid.getAuction())
          .getResultList();
      
      Bid highBid = null;
      
      for (Bid b : bids)
      {
         if (highBid == null)
         {
            highBid = b;
         }
         else if (b.getMaxAmount() > highBid.getMaxAmount())
         {
            highBid.setActualAmount(highBid.getMaxAmount());
            b.setActualAmount(Auction.getRequiredBid(highBid.getMaxAmount()));
            highBid = b;
         }
         else if (b.getMaxAmount() == highBid.getMaxAmount() &&
                  b.getBidDate().getTime() < highBid.getBidDate().getTime())
         {
            highBid.setActualAmount(highBid.getMaxAmount());
            b.setActualAmount(highBid.getMaxAmount());
            highBid = b;
         }
      }
      
      if (highBid == null)
      {
         // There are no bids so far...
         bid.setActualAmount(bid.getAuction().getRequiredBid());
         bid.getAuction().setHighBid(bid);
         outcome = "success";
      }
      else if (bid.getMaxAmount() > highBid.getMaxAmount())
      {
         // If this bid is higher than the previous maximum bid, and is from
         // a different bidder, set the actual amount to the next required bid 
         // amount for the auction
         if (!bid.getAccount().equals(highBid.getAccount()))
         {
            bid.setActualAmount(Auction.getRequiredBid(highBid.getMaxAmount()));
         }        
         else
         {
            // Otherwise don't change the amount from the bidder's last bid
            bid.setActualAmount(highBid.getActualAmount());
         }
         bid.getAuction().setHighBid(bid);         
         outcome = "success";
      }
      else
      {
         if (!bid.getAccount().equals(highBid.getAccount()))
         {
            // Set this bid, and the highest bid's, actual bid amount to this
            // bid's maximum amount
            highBid.setActualAmount(bid.getMaxAmount());
            bid.setActualAmount(bid.getMaxAmount());
            outcome = "outbid";
         }
         else
         {
            outcome = "invalid";
         }
      }
                        
      if ("success".equals(outcome) || "outbid".equals(outcome)) 
      {         
         bid.getAuction().setBids(bid.getAuction().getBids() + 1);
         
         entityManager.persist(bid);        
         
         if ("success".equals(outcome))
         {
            Conversation.instance().end();
         }
         else
         {
            Bid newBid = new Bid();
            newBid.setAuction(bid.getAuction());
            newBid.setMaxAmount(bid.getMaxAmount());
            bid = newBid;
         }
      }
      
      return outcome;
   }
      
   public String getOutcome()
   {
      return outcome;
   }
   
   public Bid getBid()
   {
      return bid;
   }   
   
   public Auction getAuction()
   {
      return auction;
   }
}
