from django.conf import settings
from django.utils import timezone
from celery.utils.log import get_task_logger
from cart.models import ChosenReward
from purist.models import AccountType
from bundlechange.models import BundleChange
from limitmonitor.common import forced_update, active_subscription, \
    get_services_from_bundle, order_paid, get_users_expired_subs
from limitmonitor.subscription import BillingPeriod, SubscriptionStatus, \
    SubscriptionManager
from middleware.common import get_woo_connection

logger = get_task_logger(__name__)


def process_pending_registrations_user(user):

    subscriptions = SubscriptionManager.search_subscriptions(user)

    if len(subscriptions) == 0:
        logger.info('User {} has no subscriptions.'.format(user.username))
        user.chosenreward.is_pending = False
        user.chosenreward.save()
        logger.info('ChosenReward for user {} set to not pending.'.format(user.username))
        return
    for sub in subscriptions:

        if sub.variation_id not in settings.WOOSUB1_PRODUCT_LIST:
            continue  # Skip this subscription as is not a product we care of

        services = get_services_from_bundle(sub.variation_id)
        if sub.status == SubscriptionStatus.ACTIVE:

            is_pending = True
            try:
                for service in services:
                    serv = user.limit_set.get(service=service)
                    serv.enable(sub.next_payment_date)
                    serv.save()
                is_pending = False
            except Exception:
                is_pending = True
            finally:
                if hasattr(user, 'chosenreward'):
                    user.chosenreward.is_pending = is_pending
                    user.chosenreward.save()
                if is_pending:
                    logger.info(
                        'Error while processing subscription {} for user {}'
                        .format(sub.subscription_id, user.username)
                    )
                else:
                    logger.info('Subscription {} for user {} was processed '
                                'successfully'.format(
                                    sub.subscription_id, user.username))

        else:
            # Log this subscription is not active
            logger.info('Subscription {} for user {} status is: {}'.format(
                sub.subscription_id, user.username, sub.status.value))


def process_pending_registrations():

    pending_regs = ChosenReward.objects.filter(is_pending=True).order_by('-id')

    for reg in pending_regs:
        process_pending_registrations_user(reg.user)


def process_renewals():

    expired_users = get_users_expired_subs()
    for user in expired_users:
        forced_update(user)


def process_bundle_changes(expired_days=14, delete=True):

    # We get all pending BundleChanges in reverse order ("-id") so we
    # process the most recent ones earlier.
    pending_changes = (
        BundleChange.objects.filter(is_pending=True)
        .filter(user__is_active=True)
        .order_by("-id")
    )

    for pending_change in pending_changes:
        process_bundle_change(pending_change,expired_days=expired_days, delete=delete)


def process_bundle_change(pending_change, expired_days=14, delete=True):

    # If the BundleChange is older than 2 weeks (default) we delete it and the associated
    # orders and subscriptions if delete==True
    if timezone.now() - pending_change.created_date > timezone.timedelta(
        days=expired_days
    ):
        # We delete the order which will also delete the subscription associated with it
        woo = get_woo_connection()
        try:
            if delete:
                result = woo.delete("orders/{}".format(pending_change.new_order_id))
            else:
                result = woo.get("orders/{}".format(pending_change.new_order_id))
            if result.status_code == 200:
                logger.info(
                    "Order {} owned by user {} is more than {} days old.{}".format(
                        pending_change.new_order_id,
                        pending_change.user,
                        expired_days,
                        " Deleted" if delete else "",
                    )
                )
                if delete:
                    pending_change.delete()
            else:
                logger.info(
                    "Order {} owned by user {} could not be {}. WooCommerce reason: {}".format(
                        pending_change.new_order_id,
                        pending_change.user,
                        result.json()["message"],
                        "deleted" if delete else "retrieved",
                    )
                )
        except Exception as e:
            logger.info(
                "Order {} owned by user {} could not be {} due to exception: {}".format(
                    pending_change.new_order_id,
                    pending_change.user,
                    e,
                    "deleted" if delete else "retrieved",
                )
            )
        return

    is_active = False
    is_paid = order_paid(pending_change.new_order_id)

    # Check if the new subscription already active
    sub = active_subscription(pending_change.user)
    if (
        sub is not None and
        sub.order_id == int(pending_change.new_order_id)
    ):
        is_active = True

    # if new subscription is active and order is paid we process
    # the bundle change
    if is_paid and is_active:
        old_sub = SubscriptionManager.get_subscription(
            sub_id=pending_change.old_subscription_id
        )
        if old_sub:
            old_sub.status = SubscriptionStatus.CANCELLED
            old_sub.save()
            # Add the remaining days of the old subscription in case of an
            # upgrade from monthly to annual subscription
            pay_accts = [AccountType.COMPLETE, AccountType.GROUP]
            if (
                sub.billing_period == BillingPeriod.ANNUAL
                and old_sub.billing_period == BillingPeriod.MONTHLY
                and old_sub.account_type in pay_accts
            ):
                if old_sub.next_payment_date == None:
                    ospd = timezone.now()
                else:
                    ospd = old_sub.next_payment_date
                next_pay_date = (ospd + timezone.timedelta(days=365))
                sub.next_payment_date = next_pay_date
                sub.start_date = ospd
                sub.save()

            forced_update(pending_change.user)

            # if this is a Family pack downgrade deal with the
            # invitees
            if pending_change.is_family_downgrade():
                downgrade_invitees(pending_change.user,
                                    old_sub.next_payment_date)

            pending_change.is_pending = False
            pending_change.save()


def downgrade_invitees(user, ren_date=None):

    invs = user.get_invitees()
    if ren_date is None:
        ren_date = timezone.now() + timezone.timedelta(days=30)

    for inv in invs:
        # Create Basic monthly subscription if the invitee has a
        # recovery email
        if inv.email != '':
            # get first active subscription that should be an Invitee
            # subscription
            i_sub = active_subscription(inv)
            if i_sub:
                if (i_sub.account_type == AccountType.INVITED):
                    # cancell invitee old subcription
                    i_sub.status = SubscriptionStatus.CANCELLED
                    i_sub.save()
                    # create new Complete subs
                    new_i_sub = SubscriptionManager.create_subscription(
                        inv, account_type=AccountType.COMPLETE, paid=True,
                        next_payment_date=ren_date,
                        status=SubscriptionStatus.ACTIVE
                    )
                    if new_i_sub is not None:
                        forced_update(inv)
                        # remove the invitation
                        invitation = user.invitation_set.get(guest=inv)
                        invitation.delete()
                else:
                    logger.info('Invitee {} current active subscription is '
                                'not of type INVITED'.format(inv.username))
            else:
                logger.info('Invitee {} does not have an active subscription'
                            .format(inv.username))
        else:
            logger.info(
                'Could not downgrade invitee {} to Complete account '
                'due to not having recovery email'.format(inv.username)
            )
