import web
import time
import channel.wechatmp.reply as reply
import channel.wechatmp.receive as receive
from config import conf
from common.log import logger
from bridge.context import *
from channel.wechatmp.common import * 
from channel.wechatmp.wechatmp_channel import WechatMPChannel

# This class is instantiated once per query
class Query():

    def GET(self):
        return verify_server(web.input())

    def POST(self):
        # Make sure to return the instance that first created, @singleton will do that. 
        channel = WechatMPChannel()
        try:
            query_time = time.time()
            webData = web.data()
            logger.debug("[wechatmp] Receive request:\n" + webData.decode("utf-8"))
            wechatmp_msg = receive.parse_xml(webData)
            if wechatmp_msg.msg_type == 'text':
                from_user = wechatmp_msg.from_user_id
                to_user = wechatmp_msg.to_user_id
                message = wechatmp_msg.content.decode("utf-8")
                message_id = wechatmp_msg.msg_id

                logger.info("[wechatmp] {}:{} Receive post query {} {}: {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), from_user, message_id, message))
                supported = True
                if "【收到不支持的消息类型,暂无法显示】" in message:
                    supported = False # not supported, used to refresh
                cache_key = from_user

                reply_text = ""
                # New request
                if cache_key not in channel.cache_dict and cache_key not in channel.running:
                    # The first query begin, reset the cache
                    context = channel._compose_context(ContextType.TEXT, message, isgroup=False, msg=wechatmp_msg)
                    logger.debug("[wechatmp] context: {} {}".format(context, wechatmp_msg))
                    if message_id in channel.received_msgs: # received and finished
                        return 
                    if supported and context:
                        # set private openai_api_key
                        # if from_user is not changed in itchat, this can be placed at chat_channel
                        user_data = conf().get_user_data(from_user)
                        context['openai_api_key'] = user_data.get('openai_api_key') # None or user openai_api_key
                        channel.received_msgs[message_id] = wechatmp_msg
                        channel.running.add(cache_key)
                        channel.produce(context)
                    else:
                        trigger_prefix = conf().get('single_chat_prefix',[''])[0]
                        if trigger_prefix or not supported:
                            if trigger_prefix:
                                content = textwrap.dedent(f"""\
                                    请输入'{trigger_prefix}'接你想说的话跟我说话。
                                    例如:
                                    {trigger_prefix}你好,很高兴见到你。""")
                            else:
                                content = textwrap.dedent("""\
                                    你好,很高兴见到你。
                                    请跟我说话吧。""")
                        else:
                            logger.error(f"[wechatmp] unknown error")
                            content = textwrap.dedent("""\
                                未知错误,请稍后再试""")
                        replyMsg = reply.TextMsg(wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content)
                        return replyMsg.send()
                    channel.query1[cache_key] = False
                    channel.query2[cache_key] = False
                    channel.query3[cache_key] = False
                # Request again
                elif cache_key in channel.running:
                    channel.query1[cache_key] = False  #To improve waiting experience, this can be set to True.
                    channel.query2[cache_key] = False  #To improve waiting experience, this can be set to True.
                    channel.query3[cache_key] = False
                elif cache_key in channel.cache_dict:
                    # Skip the waiting phase
                    channel.query1[cache_key] = True
                    channel.query2[cache_key] = True
                    channel.query3[cache_key] = True

                assert not (cache_key in channel.cache_dict and cache_key in channel.running)

                if channel.query1.get(cache_key) == False:
                    # The first query from wechat official server
                    logger.debug("[wechatmp] query1 {}".format(cache_key))
                    channel.query1[cache_key] = True
                    cnt = 0
                    while cache_key not in channel.cache_dict and cnt < 45:
                        cnt = cnt + 1
                        time.sleep(0.1)
                    if cnt == 45:
                        # waiting for timeout (the POST query will be closed by wechat official server)
                        time.sleep(1)
                        # and do nothing
                        return
                    else:
                        pass
                elif channel.query2.get(cache_key) == False:
                    # The second query from wechat official server
                    logger.debug("[wechatmp] query2 {}".format(cache_key))
                    channel.query2[cache_key] = True
                    cnt = 0
                    while cache_key not in channel.cache_dict and cnt < 45:
                        cnt = cnt + 1
                        time.sleep(0.1)
                    if cnt == 45:
                        # waiting for timeout (the POST query will be closed by wechat official server)
                        time.sleep(1)
                        # and do nothing
                        return
                    else:
                        pass
                elif channel.query3.get(cache_key) == False:
                    # The third query from wechat official server
                    logger.debug("[wechatmp] query3 {}".format(cache_key))
                    channel.query3[cache_key] = True
                    cnt = 0
                    while cache_key not in channel.cache_dict and cnt < 40:
                        cnt = cnt + 1
                        time.sleep(0.1)
                    if cnt == 40:
                        # Have waiting for 3x5 seconds
                        # return timeout message
                        reply_text = "【正在思考中,回复任意文字尝试获取回复】"
                        logger.info("[wechatmp] Three queries has finished For {}: {}".format(from_user, message_id))
                        replyPost = reply.TextMsg(from_user, to_user, reply_text).send()
                        return replyPost
                    else:
                        pass

                if float(time.time()) - float(query_time) > 4.8:
                    reply_text = "【正在思考中,回复任意文字尝试获取回复】"
                    logger.info("[wechatmp] Timeout for {} {}, return".format(from_user, message_id))
                    replyPost = reply.TextMsg(from_user, to_user, reply_text).send()
                    return replyPost
                
                if cache_key in channel.cache_dict:
                    content = channel.cache_dict[cache_key]
                    if len(content.encode('utf8'))<=MAX_UTF8_LEN:
                        reply_text = channel.cache_dict[cache_key]
                        channel.cache_dict.pop(cache_key)
                    else:
                        continue_text = "\n【未完待续,回复任意文字以继续】"
                        splits = split_string_by_utf8_length(content, MAX_UTF8_LEN - len(continue_text.encode('utf-8')), max_split= 1)
                        reply_text = splits[0] + continue_text
                        channel.cache_dict[cache_key] = splits[1]
                logger.info("[wechatmp] {}:{} Do send {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), reply_text))
                replyPost = reply.TextMsg(from_user, to_user, reply_text).send()
                return replyPost

            elif wechatmp_msg.msg_type == 'event':
                logger.info("[wechatmp] Event {} from {}".format(wechatmp_msg.Event, wechatmp_msg.from_user_id))
                content = subscribe_msg()
                replyMsg = reply.TextMsg(wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content)
                return replyMsg.send()
            else:
                logger.info("暂且不处理")
                return "success"
        except Exception as exc:
            logger.exception(exc)
            return exc