package performa.utils;

import com.stripe.Stripe;
import com.stripe.exception.APIConnectionException;
import com.stripe.exception.APIException;
import com.stripe.exception.AuthenticationException;
import com.stripe.exception.CardException;
import com.stripe.exception.InvalidRequestException;
import com.stripe.model.Card;
import com.stripe.model.Customer;
import com.stripe.model.Invoice;
import com.stripe.model.Plan;
import com.stripe.model.Subscription;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import oneit.appservices.config.ConfigMgr;
import oneit.logging.LogLevel;
import oneit.logging.LogMgr;
import oneit.logging.LoggingArea;
import oneit.objstore.StorageException;
import oneit.security.SecUser;
import oneit.utils.DateDiff;
import oneit.utils.parsers.FieldException;
import performa.orm.Company;
import performa.orm.CompanyUser;
import performa.orm.PaymentPlan;


public class StripeUtils 
{
    public static final String  STRIPE_KEY          =   ConfigMgr.getKeyfileString("stripe.key","");
    public static final String  STRIPE_PUB_KEY      =   ConfigMgr.getKeyfileString("stripe.pubkey","");
    public static final String  STRIPE_PLAN_ID      =   ConfigMgr.getKeyfileString("stripe.plan.id","0001");
    public static final String  STRIPE_COUPON_ID    =   ConfigMgr.getKeyfileString("stripe.coupon.id","EAP");
    
    static
    {
        Stripe.apiKey   =   STRIPE_KEY;
    }
    
    
    public static void createCustomer(CompanyUser companyUser) throws FieldException
    {
        try 
        {
            Company             company         =   companyUser.getCompany();
            SecUser             secUser         =   companyUser.getUser();
            Map<String, Object> customerParams  =   new HashMap<>();

            customerParams.put("description", company);
            customerParams.put("email", secUser.getEmail());
            
            Customer    customer    =   Customer.create(customerParams);
            
            company.setStripeReference(customer.getId());
            
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, "Create customer in stripe : ", customer);
        } 
        catch (StorageException | AuthenticationException | InvalidRequestException | APIConnectionException | CardException | APIException ex) 
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, ex, "Error while creating a customer in stripe");
        } 
    }
    
    
    public static Card updateCardDetails(Company company, String token) throws FieldException
    {
        try 
        {
            if(company.getStripeReference() == null)
            {
                createCustomer(company.getAddedByUser());
            }
            
            Customer customer = Customer.retrieve(company.getStripeReference());
            
            Map<String, Object> updateParams = new HashMap<>();
            
            updateParams.put("source", token);

            customer    =   customer.update(updateParams);
            
            Card    card    =   (Card) customer.getSources().retrieve(customer.getDefaultSource());
            
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, "Update card details in stripe, customer  : ", customer, " card : ", card);

            return card;
            
        } 
        catch (StorageException | AuthenticationException | InvalidRequestException | APIConnectionException | CardException | APIException ex) 
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, ex, "Error while updating a customer in stripe");
        } 
        
        return null;
    }
    
    
    public static Card retrieveCard(Company company) throws FieldException
    {
        try 
        {
            Customer customer = Customer.retrieve(company.getStripeReference());
            
            return (Card) customer.getSources().retrieve(customer.getDefaultSource());
        } 
        catch (StorageException | AuthenticationException | InvalidRequestException | APIConnectionException | CardException | APIException ex) 
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, ex, "Error while updating a customer in stripe");
        } 
        
        return null;
    }
    
    
    public static List<Invoice> retrieveInvoices(Company company) throws FieldException
    {
        try 
        {
            Map<String, Object> invoiceParams = new HashMap<>();
            
            invoiceParams.put("subscription", company.getStripeSubscription());
            
            return Invoice.list(invoiceParams).getData();
        } 
        catch (StorageException | AuthenticationException | InvalidRequestException | APIConnectionException | CardException | APIException ex) 
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, ex, "Error while retriving invoices in stripe for subscription: " + company.getStripeSubscription());
        } 
        
        return null;
    }
    
    
    public static void subscribeCustomer(Company company) throws FieldException
    {
        try 
        {
            Plan    plan        =   Plan.retrieve(STRIPE_PLAN_ID);
            Date    today       =   new Date(); 
            Date    trialExpiry =   DateDiff.add(today, Calendar.DATE, plan.getTrialPeriodDays());
            
            Map<String, Object> item    =   new HashMap<>();
            item.put("plan", STRIPE_PLAN_ID);

            Map<String, Object> items   =   new HashMap<>();
            items.put("0", item);

            Map<String, Object> params  =   new HashMap<>();
            params.put("items", items);
            params.put("coupon", STRIPE_COUPON_ID);
            params.put("customer", company.getStripeReference());
            params.put("trial_end", trialExpiry.getTime() / 1000L);

            Subscription    subscription    =   Subscription.create(params);
            
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, "Subscribing customer in stripe  : ", subscription);

            company.setStripeSubscription(subscription.getId());
        } 
        catch (StorageException | AuthenticationException | InvalidRequestException | APIConnectionException | CardException | APIException ex) 
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, ex, "Error while creating subscrition in stripe");
        } 
    }
    
    
    public static void updatePlan(Company company) throws FieldException
    {
        try 
        {
            Subscription    subscription    =   null;
            PaymentPlan     paymentPlan     =   company.getPaymentPlan();
            
            Map<String, Object> item    =   new HashMap<>();
            
            if(company.getStripeSubscription() != null)
            {
                subscription = Subscription.retrieve(company.getStripeSubscription());

                item.put("id", subscription.getSubscriptionItems().getData().get(0).getId());
            }
            
            item.put("plan", paymentPlan.getStripeReference());

            Map<String, Object> items   =   new HashMap<>();
            items.put("0", item);

            Map<String, Object> params  =   new HashMap<>();
            params.put("items", items);

            if(subscription != null)
            {
                subscription.update(params);
            }
            else 
            {
                params.put("customer", company.getStripeReference());
                
                subscription    =   Subscription.create(params);
            }
            
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, "Subscribing customer in stripe  : ", subscription);

            company.setStripeSubscription(subscription.getId());
        } 
        catch (StorageException | AuthenticationException | InvalidRequestException | APIConnectionException | CardException | APIException ex) 
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, ex, "Error while creating subscrition in stripe");
        } 
    }
}
