/*
 * Copyright 2010-2011 Ning, Inc.
 *
 * Ning licenses this file to you under the Apache License, version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at:
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.ning.billing.junction.plumbing.billing;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.catalog.api.BillingAlignment;
import com.ning.billing.catalog.api.Catalog;
import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.Product;
import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionEvent;

public class BillCycleDayCalculator {
	private static final Logger log = LoggerFactory.getLogger(BillCycleDayCalculator.class);
	
	private final CatalogService catalogService;
	private final EntitlementUserApi entitlementApi;

	@Inject
	public BillCycleDayCalculator(final CatalogService catalogService, final EntitlementUserApi entitlementApi) {
		super();
		this.catalogService = catalogService;
		this.entitlementApi = entitlementApi;
	}

	protected int calculateBcd(SubscriptionBundle bundle, Subscription subscription, final SubscriptionEvent transition, final Account account)
	throws CatalogApiException, AccountApiException, EntitlementUserApiException {

	    Catalog catalog = catalogService.getFullCatalog();
		
	    Plan prevPlan = (transition.getPreviousPlan() != null) ? catalog.findPlan(transition.getPreviousPlan(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
	    Plan nextPlan = (transition.getNextPlan() != null) ? catalog.findPlan(transition.getNextPlan(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;	    
		
		Plan plan =  (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ? nextPlan : prevPlan;
		Product product = plan.getProduct();

        PlanPhase prevPhase = (transition.getPreviousPhase() != null) ? catalog.findPhase(transition.getPreviousPhase(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
        PlanPhase nextPhase = (transition.getNextPhase() != null) ? catalog.findPhase(transition.getNextPhase(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
        
		PlanPhase phase = (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ? nextPhase : prevPhase;

		BillingAlignment alignment = catalog.billingAlignment(
		        new PlanPhaseSpecifier(product.getName(),
		                product.getCategory(),
		                phase.getBillingPeriod(),
		                transition.getNextPriceList(),
		                phase.getPhaseType()),
		                transition.getRequestedTransitionTime());

		int result = -1;
		switch (alignment) {
		case ACCOUNT :
		    result = account.getBillCycleDay();
		    if(result == 0) {
		        result = calculateBcdFromSubscription(subscription, plan, account);
		    }
		    break;
		case BUNDLE :
		    Subscription baseSub = entitlementApi.getBaseSubscription(bundle.getId());
		    Plan basePlan = baseSub.getCurrentPlan();
		    result = calculateBcdFromSubscription(baseSub, basePlan, account);
		    break;
		case SUBSCRIPTION :
		    result = calculateBcdFromSubscription(subscription, plan, account);
		    break;
		}
		if(result == -1) {
		    throw new CatalogApiException(ErrorCode.CAT_INVALID_BILLING_ALIGNMENT, alignment.toString());
		}
		return result;

	}

	private int calculateBcdFromSubscription(Subscription subscription, Plan plan, Account account) throws AccountApiException {
		DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate());
		return date.toDateTime(account.getTimeZone()).getDayOfMonth();
	}

}
