#!/usr/bin/env python
# coding: utf-8

import json
import time
import datetime

from django.db import transaction

from rpc.decorators import bind_context
from rpc.decorators import list_interface
from rpc.tool.error_code import gen, CODES

from gm_types.gaia import TIME_TYPE, RESERVATION_STATUS

from api.tasks.reservation_task import user_cancel_reservation
from api.tasks.reservation_task import user_create_reservation

from api.tool.user_tool import get_user_from_context
from api.models import (
    Schedule, Reservation, ScheduleTimeSlot,
    RESERVATION_TYPE, ORDER_STATUS,
    Order, Doctor,
)
from services.notify import notify

EARLIEST_RESERVATION = 0


@bind_context('api/reservation/list', login_required=True)
def get_avail_list(ctx, doctor_id, reserve_type):
    """
    """
    types = [reserve_type, RESERVATION_TYPE.ALL]

    try:
        doctor = Doctor.objects.get(id=doctor_id)
        if not doctor.accept_reserve:
            return gen(CODES.NOT_ACCEPT_RESERVATION)
    except Doctor.DoesNotExist:
        return gen(CODES.DOCTOR_NOT_FOUND)
    now = datetime.datetime.now()
    # 三天以后的才可预约 时间从0时开始计算
    _early_day = datetime.timedelta(days=EARLIEST_RESERVATION) + now
    early_day = datetime.datetime(_early_day.year, _early_day.month,
                                  _early_day.day, 0, 0, 0)

    # 获得医生设置的所有的天数
    schedule_data = Schedule.objects.filter(
        doctor_id=doctor_id,
        reservation_type__in=types,
        date__gte=early_day
    ).order_by('date', 'start_time')

    if not schedule_data:
        return gen(CODES.NO_AVAIL_SLOTS)

    # 过滤掉比当前时间小的可用时间
    filter_slots = ScheduleTimeSlot.objects.filter(
        start_time__gte=now,
        reservation_id__isnull=True,
        doctor_id=doctor_id
    ).order_by('start_time')

    # 按照医生设置的日期遍历 组织数据
    avail = False
    date_frame = []
    for info in schedule_data:
        ampm = info.am_or_pm
        address = info.address
        year = info.date.year
        month = info.date.month
        day = info.date.day

        start_time = datetime.datetime.combine(info.date, info.start_time)
        end_time = datetime.datetime.combine(info.date, info.end_time)

        avail_slot = filter_slots.filter(start_time__range=(start_time, end_time))

        operate_hour = []
        if str(reserve_type) == RESERVATION_TYPE.SURGERY:
            # 手术可用连续timeslot判断
            start_slot = 0
            counter = 0
            year = month = day = hour = 0
            for slot in avail_slot:
                date = slot.start_time
                if date.year != year or date.month != month or date.day != day or date.hour != hour:
                    counter = 1
                    year = date.year
                    month = date.month
                    day = date.day
                    hour = date.hour
                    start_slot = slot.id
                    continue
                else:
                    counter += 1
                if counter == 3:
                    operate_hour.append({'id': start_slot,
                                         'time': slot.start_time.strftime('%H:00'),
                                         'full_time': slot.start_time.strftime('%Y-%m-%d %H:00')})
                    counter = 0
        else:
            # 面诊直接添加每个timeslot
            for slot in avail_slot:
                operate_hour.append({
                    'id': slot.id,
                    'time': slot.start_time.strftime('%H:%M'),
                    'full_time': slot.start_time.strftime('%Y-%m-%d %H:%M')
                })

        if len(operate_hour):
            avail = True

        if operate_hour:
            date_frame.append({
                'date': time.mktime(info.date.timetuple()),
                'address': {
                    'lng': address.lng,
                    'lat': address.lat,
                    'name': address.desc,
                    'location': address.location,
                },
                'time_frame': operate_hour,
            })

    if not avail:
        return gen(CODES.NO_AVAIL_SLOTS)
    return date_frame


@bind_context('api/reservation/detail')
def reservation_detail(ctx, reservation_id):
    user = get_user_from_context(ctx)
    try:
        info = Reservation.objects.get(id=reservation_id)
    except Reservation.DoesNotExist:
        return gen(CODES.NO_RESERVATION_DETAIL)
    if info.user != user:
        return gen(CODES.NO_PERMISSION)
    Reservation.check_reservation_expired(user)
    schedule_info = info.schedule
    address = schedule_info.address
    data = {
        'doctor_id': schedule_info.doctor.id,
        'doctor_name': schedule_info.doctor.name,
        'order': json.loads(info.order.service_snapshot).get('name'),
        'date': time.mktime(info.date.timetuple()),
        'address': {
            'lng': address.lng,
            'lat': address.lat,
            'name': address.desc,
            'location': address.location,
        },
        'type': info.reservation_type,
        'status': info.status,
        'reservation_id': info.id,
    }
    return data


@bind_context('api/reservation/mine')
@list_interface(offset_name='start_num', limit_name='count', element_model=Reservation)
def my_reservations(ctx, start_num, count):
    user = get_user_from_context(ctx)
    Reservation.check_reservation_expired(user)
    mine = Reservation.objects.filter(user=user).order_by('date')
    if not mine:
        return gen(CODES.NO_RESERVATION_DATA)

    mine = mine[start_num:start_num + count]
    data = []
    for one in mine:
        info = {
            'doctor_name': one.schedule.doctor.name,
            'date': time.mktime(one.date.timetuple()),
            'type': one.reservation_type,
            'status': one.status,
            'id': one.id,
        }
        data.append(info)
    return data


@bind_context('api/reservation/create')
def reservation_create(ctx, order_id, time_frame, reservation_type):
    user = get_user_from_context(ctx)

    try:
        order_info = Order.objects.get(id=order_id, user=user)
    except Order.DoesNotExist:
        return gen(CODES.ORDER_NOT_FOUND)  # 未找到该订单

    doctor_id = order_info.service.doctor.id
    if order_info.status != ORDER_STATUS.PAID:
        return gen(CODES.ORDER_WRONG_STATUS_CODE)  # 只在订单PAID时才能预约

    reservation_info = Reservation.objects.filter(order_id=order_id).order_by('-date')
    if reservation_info and reservation_info[0].status not in [
            RESERVATION_STATUS.EXPIRED, RESERVATION_STATUS.CANCELED]:
        return gen(CODES.RESERVED)  # 已经预约

    try:
        with transaction.atomic():
            slot_info = ScheduleTimeSlot.objects.get(id=time_frame, reservation__isnull=True)
            date = slot_info.start_time
            ampm = ''
            if datetime.time(0, 0, 0) <= date.time() <= datetime.time(12, 0, 0):
                ampm = TIME_TYPE.AM
            else:
                ampm = TIME_TYPE.PM
            try:
                schedule = Schedule.objects.get(
                    doctor_id=doctor_id,
                    date__year=date.year,
                    date__month=date.month,
                    date__day=date.day,
                    am_or_pm=ampm,
                    start_time__lte=slot_info.start_time,
                )
            except Schedule.DoesNotExist:
                return gen(CODES.NO_AVAIL_SLOTS)  # schedule未找到

            address = {
                'desc': schedule.address.desc,
                'lng': schedule.address.lng,
                'lat': schedule.address.lat,
                'location': schedule.address.location
            }
            reservation = Reservation.objects.create(
                date=date,
                schedule_id=schedule.id,
                order_id=order_info.id,
                status=RESERVATION_STATUS.RESERVING,
                user=user,
                created_time=datetime.datetime.now(),
                reservation_address=address,
                reservation_type=reservation_type
            )

            if reservation_type == RESERVATION_TYPE.DIAGNOSE:
                slot_info.reservation = reservation
                slot_info.save()  # 修改slot_info的状态
            elif reservation_type == RESERVATION_TYPE.SURGERY:
                year = date.year
                month = date.month
                day = date.day
                hour = date.hour
                start_time = datetime.datetime(year, month, day, hour)
                end_time = datetime.datetime(year, month, day, hour, 59)
                slots = ScheduleTimeSlot.objects.filter(
                    start_time__gte=start_time,
                    start_time__lte=end_time,
                    doctor_id=doctor_id,
                    reservation_id__isnull=True
                )
                if len(slots) < 3:
                    return gen(CODES.NO_AVAIL_SLOTS)  # 可用slot不足
                slots.update(reservation_id=reservation)
            reservation.save()  # 创建reservation
            user_create_reservation(reservation)
            notify('reserve/add', doctor_id=doctor_id)
    except ScheduleTimeSlot.DoesNotExist:
        return gen(CODES.NO_AVAIL_SLOTS)  # 可用slot不足
    return gen(CODES.SUCCESS)


@bind_context('api/reservation/cancel')
def reservation_cancel(ctx, reservation_id):
    auth_user = get_user_from_context(ctx)
    try:
        data = Reservation.objects.get(id=reservation_id, user=auth_user)
    except Reservation.DoesNotExist:
        return gen(CODES.NO_RESERVE_STATUS)

    data.cancel_operator_role_id = auth_user
    data.status = RESERVATION_STATUS.CANCELED
    data.cancel_time = datetime.datetime.now()
    slots = ScheduleTimeSlot.objects.filter(reservation_id=data.id)
    slots.update(reservation_id=None)
    data.save()
    user_cancel_reservation(data)
    doctor_id = data.order.service.doctor_id
    notify('reserve/delete', doctor_id=doctor_id)
    return gen(CODES.SUCCESS)
