import urllib

# from py12306.config import UserType
from py12306.config import Config
from py12306.helpers.api import *
from py12306.helpers.func import *
from py12306.helpers.notification import Notification
from py12306.helpers.type import UserType, SeatType
from py12306.log.common_log import CommonLog
from py12306.log.order_log import OrderLog


class Order:
    """
    处理下单
    """
    session = None
    query_ins = None
    user_ins = None

    passenger_ticket_str = ''
    old_passenger_str = ''

    is_need_auth_code = False

    max_queue_wait = 60 * 5  # 最大排队时长
    current_queue_wait = 0
    retry_time = 3
    wait_queue_interval = 3

    order_id = 0

    notification_sustain_time = 60 * 30  # 通知持续时间 30 分钟
    notification_interval = 5 * 60  # 通知间隔

    def __init__(self, query, user):
        self.session = user.session
        from py12306.query.job import Job
        from py12306.user.job import UserJob
        assert isinstance(query, Job)
        assert isinstance(user, UserJob)
        self.query_ins = query
        self.user_ins = user

        self.make_passenger_ticket_str()

    def order(self):
        """
        开始下单
        下单模式  暂时不清楚，使用正常步骤下单
        :return:
        """
        # Debug
        if Config().IS_DEBUG:
            self.order_id = 'test'
            self.order_did_success()
            return random.randint(0, 10) > 7
        return self.normal_order()

    def normal_order(self):
        if not self.submit_order_request(): return
        if not self.user_ins.request_init_dc_page(): return
        if not self.check_order_info(): return
        if not self.get_queue_count(): return
        if not self.confirm_single_for_queue(): return
        order_id = self.query_order_wait_time()
        if order_id:  # 发送通知
            self.order_id = order_id
            self.order_did_success()
            return True
        return False

    def order_did_success(self):
        OrderLog.print_ticket_did_ordered(self.order_id)
        OrderLog.notification(OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE,
                              OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_CONTENT)
        self.send_notification()

    def send_notification(self):
        # num = 0  # 通知次数
        # sustain_time = self.notification_sustain_time
        if Config().EMAIL_ENABLED:  # 邮件通知
            Notification.send_email(Config().EMAIL_RECEIVER, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE,
                                    OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id))
        if Config().DINGTALK_ENABLED:  # 钉钉通知
            Notification.dingtalk_webhook(
                OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id))
        if Config().TELEGRAM_ENABLED:  # Telegram推送
            Notification.send_to_telegram(
                OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id))
        if Config().SERVERCHAN_ENABLED:  # ServerChan通知
            Notification.server_chan(Config().SERVERCHAN_KEY, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE,
                                     OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id))
        if Config().PUSHBEAR_ENABLED:  # PushBear通知
            Notification.push_bear(Config().PUSHBEAR_KEY, OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_TITLE,
                                   OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_EMAIL_CONTENT.format(self.order_id))

        if Config().NOTIFICATION_BY_VOICE_CODE:  # 语音通知
            OrderLog.add_quick_log(OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_START_SEND.format(num))
            Notification.voice_code(Config().NOTIFICATION_VOICE_CODE_PHONE, self.user_ins.get_name(),
                                    OrderLog.MESSAGE_ORDER_SUCCESS_NOTIFICATION_OF_VOICE_CODE_CONTENT.format(
                                        self.query_ins.left_station, self.query_ins.arrive_station))
        # 取消循环发送通知
        # while sustain_time:  # TODO 后面直接查询有没有待支付的订单就可以
        #     num += 1
        #     else:
        #         break
        #     sustain_time -= self.notification_interval
        #     sleep(self.notification_interval)

        OrderLog.add_quick_log(OrderLog.MESSAGE_JOB_CLOSED).flush()

    def submit_order_request(self):
        data = {
            'secretStr': urllib.parse.unquote(self.query_ins.get_info_of_secret_str()),  # 解密
            'train_date': self.query_ins.left_date,  # 出发时间
            'back_train_date': self.query_ins.left_date,  # 返程时间
            'tour_flag': 'dc',  # 旅途类型
            'purpose_codes': 'ADULT',  # 成人 | 学生
            'query_from_station_name': self.query_ins.left_station,
            'query_to_station_name': self.query_ins.arrive_station,
        }
        response = self.session.post(API_SUBMIT_ORDER_REQUEST, data)
        result = response.json()
        if result.get('data') == 'N':
            OrderLog.add_quick_log(OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS).flush()
            return True
        else:
            if (str(result.get('messages', '')).find('未处理') >= 0):  # 未处理订单
                self.order_id = 0  # 需要拿到订单号 TODO
                return self.order_did_success()
            OrderLog.add_quick_log(
                OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_FAIL.format(
                    result.get('messages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush()
        return False

    def check_order_info(self):
        """
        cancel_flag=2
        bed_level_order_num=000000000000000000000000000000
        passengerTicketStr=
        tour_flag=dc
        randCode=
        whatsSelect=1
        _json_att=
        REPEAT_SUBMIT_TOKEN=458bf1b0a69431f34f9d2e9d3a11cfe9
        :return:
        """
        data = {  #
            'cancel_flag': 2,
            'bed_level_order_num': '000000000000000000000000000000',
            'passengerTicketStr': self.passenger_ticket_str,
            'oldPassengerStr': self.old_passenger_str,
            'tour_flag': 'dc',
            'randCode': '',
            'whatsSelect': '1',
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token
        }
        response = self.session.post(API_CHECK_ORDER_INFO, data)
        result = response.json()
        if result.get('data.submitStatus'):  # 成功
            # ifShowPassCode 需要验证码
            OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_SUCCESS).flush()
            if result.get('data.ifShowPassCode') != 'N':
                self.is_need_auth_code = True

            # if ( ticketInfoForPassengerForm.isAsync == ticket_submit_order.request_flag.isAsync & & ticketInfoForPassengerForm.queryLeftTicketRequestDTO.ypInfoDetail != "") { 不需要排队检测 js TODO
            return True
        else:
            error = CommonLog.MESSAGE_API_RESPONSE_CAN_NOT_BE_HANDLE
            if not result.get('data.isNoActive'):
                error = result.get('data.errMsg', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR)
            else:
                if result.get('data.checkSeatNum'):
                    error = '无法提交您的订单! ' + result.get('data.errMsg')
                else:
                    error = '出票失败! ' + result.get('data.errMsg')
            OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_FAIL.format(error)).flush()
        return False

    def get_queue_count(self):
        """
        获取队列人数
        train_date	Mon Jan 01 2019 00:00:00 GMT+0800 (China Standard Time)
        train_no	630000Z12208
        stationTrainCode	Z122
        seatType	4
        fromStationTelecode	GZQ
        toStationTelecode	RXW
        leftTicket	CmDJZYrwUoJ1jFNonIgPzPFdMBvSSE8xfdUwvb2lq8CCWn%2Bzk1vM3roJaHk%3D
        purpose_codes	00
        train_location	QY
        _json_att
        REPEAT_SUBMIT_TOKEN	0977caf26f25d1da43e3213eb35ff87c
        :return:
        """
        data = {  #
            'train_date': '{} 00:00:00 GMT+0800 (China Standard Time)'.format(
                datetime.datetime.strptime(self.query_ins.left_date, '%Y-%m-%d').strftime("%a %h %d %Y")),
            'train_no': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO']['train_no'],
            'stationTrainCode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][
                'station_train_code'],
            'seatType': self.query_ins.current_order_seat,
            'fromStationTelecode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][
                'from_station'],
            'toStationTelecode': self.user_ins.ticket_info_for_passenger_form['queryLeftTicketRequestDTO'][
                'to_station'],
            'leftTicket': self.user_ins.ticket_info_for_passenger_form['leftTicketStr'],
            'purpose_codes': self.user_ins.ticket_info_for_passenger_form['purpose_codes'],
            'train_location': self.user_ins.ticket_info_for_passenger_form['train_location'],
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token,
        }
        response = self.session.post(API_GET_QUEUE_COUNT, data)
        result = response.json()
        if result.get('status', False):  # 成功
            """
            "data": { 
                "count": "66",
                "ticket": "0,73", 
                "op_2": "false",
                "countT": "0",
                "op_1": "true"
            }
            
            """
            # if result.get('isRelogin') == 'Y': # 重新登录 TODO

            ticket = result.get('data.ticket').split(',')  # 余票列表
            # 这里可以判断 是真实是 硬座还是无座，避免自动分配到无座
            ticket_number = ticket[0]  # 余票
            if ticket_number != '充足' or int(ticket_number) <= 0:
                if self.query_ins.current_seat == SeatType.NO_SEAT:  # 允许无座
                    ticket_number = ticket[1]

            if result.get('data.op_2') == 'true':
                OrderLog.add_quick_log(OrderLog.MESSAGE_GET_QUEUE_LESS_TICKET).flush()
                return False

            current_position = int(result.get('data.countT', 0))
            OrderLog.add_quick_log(
                OrderLog.MESSAGE_GET_QUEUE_INFO_SUCCESS.format(current_position, ticket_number)).flush()
            return True
        else:
            # 加入小黑屋
            OrderLog.add_quick_log(OrderLog.MESSAGE_GET_QUEUE_COUNT_FAIL.format(
                result.get('messages', result.get('validateMessages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR)))).flush()
        return False

    def confirm_single_for_queue(self):
        """
        确认排队
        passengerTicketStr
        oldPassengerStr
        randCode
        purpose_codes	00
        key_check_isChange	FEE6C6634A3EAA93E1E6CFC39A99E555A92E438436F18AFF78837CDB
        leftTicketStr	CmDJZYrwUoJ1jFNonIgPzPFdMBvSSE8xfdUwvb2lq8CCWn%2Bzk1vM3roJaHk%3D
        train_location	QY
        choose_seats
        seatDetailType	000
        whatsSelect	1
        roomType	00
        dwAll	N
        _json_att
        REPEAT_SUBMIT_TOKEN	0977caf26f25d1da43e3213eb35ff87c
        :return:
        """
        data = {  #
            'passengerTicketStr': self.passenger_ticket_str,
            'oldPassengerStr': self.old_passenger_str,
            'randCode': '',
            'purpose_codes': self.user_ins.ticket_info_for_passenger_form['purpose_codes'],
            'key_check_isChange': self.user_ins.ticket_info_for_passenger_form['key_check_isChange'],
            'leftTicketStr': self.user_ins.ticket_info_for_passenger_form['leftTicketStr'],
            'train_location': self.user_ins.ticket_info_for_passenger_form['train_location'],
            'choose_seats': '',
            'seatDetailType': '000',
            'whatsSelect': '1',
            'roomType': '00',
            'dwAll': 'N',
            '_json_att': '',
            'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token,
        }

        if self.is_need_auth_code:  # 目前好像是都不需要了，有问题再处理
            pass

        response = self.session.post(API_CONFIRM_SINGLE_FOR_QUEUE, data)
        result = response.json()

        if 'data' in result:
            """
           "data": {
                "submitStatus": true
            }
            """
            if result.get('data.submitStatus'):  # 成功
                OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_SUCCESS).flush()
                return True
            else:
                # 加入小黑屋 TODO
                OrderLog.add_quick_log(
                    OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR.format(
                        result.get('data.errMsg', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush()
        else:
            OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_FAIL.format(
                result.get('messages', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush()
        return False

    def query_order_wait_time(self):
        """
        排队查询
        random	1546849953542
        tourFlag	dc
        _json_att
        REPEAT_SUBMIT_TOKEN	0977caf26f25d1da43e3213eb35ff87c
        :return:
        """
        self.current_queue_wait = self.max_queue_wait
        self.queue_num = 0
        while self.current_queue_wait:
            self.current_queue_wait -= self.wait_queue_interval
            self.queue_num += 1
            # TODO 取消超时订单，待优化
            data = {  #
                'random': str(random.random())[2:],
                'tourFlag': 'dc',
                '_json_att': '',
                'REPEAT_SUBMIT_TOKEN': self.user_ins.global_repeat_submit_token,
            }

            response = self.session.get(API_QUERY_ORDER_WAIT_TIME.format(urllib.parse.urlencode(data)))
            result = response.json()

            if result.get('status') and 'data' in result:
                """
               "data": {
                    "queryOrderWaitTimeStatus": true,
                    "count": 0,
                    "waitTime": -1,
                    "requestId": 6487958947291482523,
                    "waitCount": 0,
                    "tourFlag": "dc",
                    "orderId": "E222646122"
                }
                """
                result_data = result['data']
                order_id = result_data.get('orderId')
                if order_id:  # 成功
                    return order_id
                elif 'waitTime' in result_data:
                    # 计算等待时间
                    wait_time = int(result_data.get('waitTime'))
                    if wait_time == -1 or wait_time == -100:  # 成功
                        # /otn/confirmPassenger/resultOrderForDcQueue 请求订单状态 目前不需要
                        # 不应该走到这
                        return order_id
                    elif wait_time >= 0:  # 等待
                        OrderLog.add_quick_log(
                            OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_WAITING.format(result_data.get('waitCount', 0),
                                                                                  wait_time)).flush()
                    else:
                        if wait_time == -2 or wait_time == -3:  # -2 失败 -3 订单已撤销
                            OrderLog.add_quick_log(
                                OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format(result_data.get('msg'))).flush()
                        else:  # 未知原因
                            OrderLog.add_quick_log(
                                OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format(
                                    result_data.get('msg', wait_time))).flush()

                elif result_data.get('msg'):  # 失败 对不起，由于您取消次数过多，今日将不能继续受理您的订票请求。1月8日您可继续使用订票功能。
                    # TODO 需要增加判断 直接结束
                    OrderLog.add_quick_log(
                        OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format(
                            result_data.get('msg', CommonLog.MESSAGE_RESPONSE_EMPTY_ERROR))).flush()
                    stay_second(self.retry_time)
                    return False
            elif result.get('messages') or result.get('validateMessages'):
                OrderLog.add_quick_log(OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_FAIL.format(
                    result.get('messages', result.get('validateMessages')))).flush()
            else:
                pass
            OrderLog.add_quick_log(OrderLog.MESSAGE_QUERY_ORDER_WAIT_TIME_INFO.format(self.queue_num)).flush()
            stay_second(self.wait_queue_interval)

        return False

    def make_passenger_ticket_str(self):
        """
        生成提交车次的内容
        格式：
        1(seatType),0,1(车票类型:ticket_type_codes),张三(passenger_name),1(证件类型:passenger_id_type_code),xxxxxx(passenger_id_no),xxxx(mobile_no),N
        passengerTicketStr:
        张三(passenger_name),1(证件类型:passenger_id_type_code),xxxxxx(passenger_id_no),1_
        oldPassengerStr
        :return:
        """
        passenger_tickets = []
        old_passengers = []
        available_passengers = self.query_ins.passengers
        if len(available_passengers) > self.query_ins.member_num_take:  # 删除人数
            available_passengers = available_passengers[0:self.query_ins.member_num_take]
            OrderLog.print_passenger_did_deleted(available_passengers)

        for passenger in available_passengers:
            tmp_str = '{seat_type},0,{passenger_type},{passenger_name},{passenger_id_card_type},{passenger_id_card},{passenger_mobile},N_'.format(
                seat_type=self.query_ins.current_order_seat, passenger_type=passenger['type'],
                passenger_name=passenger['name'],
                passenger_id_card_type=passenger['id_card_type'], passenger_id_card=passenger['id_card'],
                passenger_mobile=passenger['mobile']
            )
            passenger_tickets.append(tmp_str)

            if int(passenger['type']) != UserType.CHILD:
                tmp_old_str = '{passenger_name},{passenger_id_card_type},{passenger_id_card},{passenger_type}_'.format(
                    passenger_name=passenger['name'],
                    passenger_id_card_type=passenger['id_card_type'], passenger_id_card=passenger['id_card'],
                    passenger_type=passenger['type'],
                )
                old_passengers.append(tmp_old_str)

        self.passenger_ticket_str = ''.join(passenger_tickets).rstrip('_')
        self.old_passenger_str = ''.join(old_passengers).rstrip('_') + '__ _ _'  # 不加后面请求会出错
