"""
point system api portal
"""

import datetime
from datetime import date, timedelta

from gm_types.gaia import (
    POINTS_OPERATION,
    POINTS_TYPE,
)

from point.point_tool import points_operate, deduction_to_points
from point.consts import points_reason_rule
from point.models import Points


__all__ = [
    'add',
    'remove',
    'exist',
    'get_point_for',
    'get_user_current_month_sign_day',
    'deduction_to_points',
]


def _validate_reason(reason):
    assert reason in POINTS_TYPE


def exist(user_id, reason):
    """check if user has this type of point gain.

    .. params::
        user_id: int
        reason: gm_types.gaia.POINTS_TYPE
    """
    return Points.objects.filter(user_id=user_id, reason=reason).exists()


def add(user_id, reason, point=None, order_id=None, link_user_id=None, is_new_sign_in=False):
    """add point for user.

    .. params::
        user_id: int
        reason: gm_types.gaia.POINTS_TYPE
        point: int
        order_id: int
        link_user_id: int, invited by this user
    """
    # check reason we support
    _validate_reason(reason)

    if reason in (POINTS_TYPE.CANCEL_ORDER, POINTS_TYPE.PAY) and order_id and point:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.ADD,
            reason=reason,
            link_order_id=order_id
        )

    elif reason == POINTS_TYPE.SIGN_IN and point:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.ADD,
            reason=reason,
            is_new_sign_in=is_new_sign_in
        )

    elif reason == POINTS_TYPE.INVITE and link_user_id:
        point = points_reason_rule.get(reason, 0)
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.ADD,
            link_user_id=link_user_id
        )

    elif reason == POINTS_TYPE.GET_POINTS_FROM_LOTTERY and point > 0:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.ADD,
            reason=reason,
            is_new_sign_in=is_new_sign_in
        )
    elif reason == POINTS_TYPE.LIPSTICK_POINTS and point > 0:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.ADD,
            reason=reason,
            is_new_sign_in=is_new_sign_in
        )
    else:
        point = points_reason_rule.get(reason, 0)
        # check point
        if point == 0:
            return 0

        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.ADD,
            reason=reason
        )

    return point


def remove(user_id, reason, point=None, order_id=None):
    """remove point for user.

    .. params::
        user_id: int
        reason: gm_types.gaia.POINTS_TYPE
        point: int
        order_id: int
    """
    _validate_reason(reason)

    if reason == POINTS_TYPE.BUY_SERVICE and order_id and point:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.REMOVE,
            reason=reason,
            link_order_id=order_id
        )

    if reason == POINTS_TYPE.POINTS_EXCHANGE and point:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.REMOVE,
            reason=reason,
            link_order_id=order_id
        )

    if reason == POINTS_TYPE.POINTS_LOTTERY and point:
        points_operate(
            user=user_id,
            num=point,
            operation=POINTS_OPERATION.REMOVE,
            reason=reason,
            link_order_id=order_id
        )

    return point


def get_point_for(user):
    """get point for user.

    .. params::
        user: User Object

    NOTE:
        user with caution, this will be update.

    TODO:
        record user points in point system
    """
    point = user and user.person.points or 0
    return point



def get_first_day(dt, d_years=0, d_months=0):
    y, m = dt.year + d_years, dt.month + d_months
    a, m = divmod(m-1, 12)
    return date(y+a, m+1, 1)


def get_last_day(dt):
    return get_first_day(dt, 0, 1) + timedelta(-1)


def get_user_current_month_sign_day(user_id):
    today = datetime.date.today()
    first_day = get_first_day(today)
    last_day = get_last_day(today)
    date_range = (first_day.strftime('%Y-%m-%d'), last_day.strftime('%Y-%m-%d 23:59:59'))

    get_points_days = Points.objects.filter(
        add_time__range=date_range,
        reason=POINTS_TYPE.SIGN_IN,
        user_id=user_id
    ).values_list('add_time', flat=True)
    get_points_days = [day.day for day in get_points_days]
    return get_points_days
