import time

import web

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


# 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" or wechatmp_msg.msg_type == "voice":
                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
                        # no return because of bandwords or other reasons
                        return "success"
                    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
                # User request again, and the answer is not ready
                elif (
                    cache_key in channel.running
                    and channel.query1.get(cache_key) == True
                    and channel.query2.get(cache_key) == True
                    and channel.query3.get(cache_key) == True
                ):
                    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
                # User request again, and the answer is ready
                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 in channel.running 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 in channel.running 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 in channel.running 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 (
                    cache_key not in channel.cache_dict
                    and cache_key not in channel.running
                ):
                    # no return because of bandwords or other reasons
                    return "success"

                # 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.content, 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