package performa.form;

import com.stripe.model.Subscription;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import oneit.logging.LogLevel;
import oneit.logging.LogMgr;
import oneit.objstore.StorageException;
import oneit.objstore.parser.BusinessObjectParser;
import oneit.servlets.forms.SubmissionDetails;
import oneit.servlets.forms.SuccessfulResult;
import oneit.servlets.process.ORMProcessState;
import oneit.servlets.process.SaveFP;
import oneit.utils.BusinessException;
import oneit.utils.CollectionUtils;
import oneit.utils.MultiException;
import performa.orm.HiringTeam;
import performa.orm.PaymentPlan;
import performa.utils.StripeUtils;


public class SavePaymentPlanFP extends SaveFP 
{
    @Override
    public SuccessfulResult processForm(ORMProcessState process, SubmissionDetails submission, Map params) throws BusinessException, StorageException
    {
        HttpServletRequest  request         =   submission.getRequest();
        HiringTeam          hiringTeam      =   (HiringTeam) process.getAttribute("HiringTeam");
        PaymentPlan         paymentPlan     =   request.getAttribute("PaymentPlan") != null ? (PaymentPlan) request.getAttribute("PaymentPlan") : hiringTeam.getPaymentPlan();
        Boolean             firstTime       =   request.getAttribute("firstTime") != null ? (Boolean) request.getAttribute("firstTime") : Boolean.FALSE;
        Boolean             savePlan        =   request.getAttribute("savePlan") != null ? (Boolean) request.getAttribute("savePlan") : Boolean.FALSE;
        Boolean             savePPJ         =   request.getAttribute("savePPJ") != null ? (Boolean) request.getAttribute("savePPJ") : Boolean.FALSE;
        Boolean             saveCap         =   request.getAttribute("saveCap") != null ? (Boolean) request.getAttribute("saveCap") : Boolean.FALSE;

        // Billing needs to be setup first. Cannot subscribe a user to a plan without card details
        if(hiringTeam.getCardID() == null)
        {
            throw new BusinessException("Please enter billing details before selecting a payment plan");
        }
            
        Subscription        subscription    =   StripeUtils.retrieveSubscription(hiringTeam.getStripeSubscription());
        
        if(subscription != null && savePPJ)
        {
            if(CollectionUtils.equals(hiringTeam.getIsPPJ(), Boolean.TRUE))
            {
                StripeUtils.cancelSubscription(subscription, true);

                hiringTeam.setHasCap(false);
                hiringTeam.setMaxCap(null);
                hiringTeam.setPlanCancelled(true);

                LogMgr.log(HiringTeam.LOG, LogLevel.PROCESSING1,"In SaveCompanyFP cancelling a subscription in Stripe since moving to PPJ : ", subscription );
            }
            else
            {
                if(hiringTeam.getPaymentPlan() != null && hiringTeam.isTrue(hiringTeam.getPlanCancelled()))
                {
                    StripeUtils.cancelSubscription(subscription, false);

                    hiringTeam.setPlanCancelled(false);

                    LogMgr.log(HiringTeam.LOG, LogLevel.PROCESSING1,"In SaveCompanyFP reactivating a subscription (as subscription is not cancelled yet) in Stripe since moving to Subscription from PPJ : ", subscription );
                }
                
            }
        }
        
        // Payment plan value might get set if apply coupon button is clicked after selecting a plan, and before saving plan.
        // After that if PPJ is selected, need to reset plan to null - as no subscription is made at this point - S30819045
        if(hiringTeam.isTrue(hiringTeam.getIsPPJ()) && subscription == null && hiringTeam.getPaymentPlan() != null)
        {
            hiringTeam.setPaymentPlan(null);
            hiringTeam.setMaxCap(0);
            hiringTeam.setHasCap(false);
        }
        
        // When Subscribe and Save selected
        if(CollectionUtils.equals(hiringTeam.getIsPPJ(), Boolean.FALSE) && (firstTime || savePlan))
        {
            LogMgr.log(HiringTeam.LOG, LogLevel.PROCESSING1,"Hiring Team payment plan updated.", hiringTeam, " payment plan: ", hiringTeam.getPaymentPlan());

            Subscription    updatedSubscription =   StripeUtils.updatePlan(hiringTeam, subscription, paymentPlan);

            if(updatedSubscription == null)
            {
                throw new BusinessException("Problem with changing your plan. Please contact adminstrator for more info.");
            }
            
            if(firstTime)
            {
                hiringTeam.setAvailableCredits(paymentPlan.getActiveJobCount());
            }
            else
            {
                PaymentPlan currentPlan =   hiringTeam.getPaymentPlan();

                if(subscription != null && currentPlan != null && currentPlan.getActiveJobCount() < paymentPlan.getActiveJobCount())
                {
                    boolean hasValidCoupon  =   hiringTeam.hasValidCouponOn(new Date(subscription.getCurrentPeriodEnd() * 1000));
                    double  currentPlanCost =   hiringTeam.getLastPlanAmount() != null ? hiringTeam.getLastPlanAmount() : currentPlan.getAmount();
                    double  newPlanCost     =   hasValidCoupon ?  paymentPlan.getAmount() * (1 - (hiringTeam.getCoupon().getPercentageOff() * 0.01)) : paymentPlan.getAmount();
                    double  costDiff        =   newPlanCost - currentPlanCost;

                    StripeUtils.chargeUpgradePlanDifference(hiringTeam, costDiff);
                    
                    hiringTeam.setAvailableCredits(paymentPlan.getActiveJobCount());
                }
                
                hiringTeam.setPaymentPlan(paymentPlan);
            }
                
            double  discountPercentage  =   0d;

            if(subscription != null && subscription.getDiscount() != null && subscription.getDiscount().getCoupon() != null && subscription.getDiscount().getCoupon().getPercentOff() != null)
            {
                discountPercentage  = 1 - (subscription.getDiscount().getCoupon().getPercentOff().doubleValue() * 0.01 );
            }

            hiringTeam.setLastPlanAmount(discountPercentage > 0 ?  paymentPlan.getAmount() * discountPercentage : paymentPlan.getAmount());

            LogMgr.log(HiringTeam.LOG, LogLevel.PROCESSING1,"Stripe subscription updated.", hiringTeam, hiringTeam.getStripeSubscription());
        }
        
        if(!hiringTeam.isTrue(hiringTeam.getHasCap()))
        {
            hiringTeam.setMaxCap(null);
        }
        
        if((firstTime || saveCap) && (hiringTeam.isTrue(hiringTeam.getHasCap()) && hiringTeam.getPaymentPlan() != null))
        {
            if(hiringTeam.getMaxCap() == null)
            {
                hiringTeam.setMaxCap(hiringTeam.getPaymentPlan().getActiveJobCount());
            }
            else if(hiringTeam.getMaxCap() < hiringTeam.getPaymentPlan().getActiveJobCount())
            {
                throw new BusinessException("Cap should be greater than the number of jobs of the selected plan");
            }
        }
        
        return super.processForm(process, submission, params);
    }
    
    @Override
    public void validate(ORMProcessState process, SubmissionDetails submission, MultiException exceptions, Map params) throws StorageException
    {
        HttpServletRequest  request     =   submission.getRequest();
        HiringTeam          hiringTeam  =   (HiringTeam) process.getAttribute("HiringTeam");
        PaymentPlan         paymentPlan =   request.getAttribute("PaymentPlan") != null ? (PaymentPlan) request.getAttribute("PaymentPlan") : hiringTeam.getPaymentPlan();
        Boolean             firstTime   =   request.getAttribute("firstTime") != null ? (Boolean) request.getAttribute("firstTime") : Boolean.FALSE;
        Boolean             savePlan    =   request.getAttribute("savePlan") != null ? (Boolean) request.getAttribute("savePlan") : Boolean.FALSE;
        
        if(CollectionUtils.equals(hiringTeam.getIsPPJ(), Boolean.FALSE) && (firstTime || savePlan))
        {
            BusinessObjectParser.assertFieldCondition(paymentPlan != null, hiringTeam , HiringTeam.SINGLEREFERENCE_PaymentPlan, "mandatory", exceptions, true, request);
        }
        
        super.validate(process, submission, exceptions, params);
    }
}