#!/usr/bin/env python3
import json
from socket import socket, AF_INET, SOCK_STREAM
from collections import namedtuple
from .base import BaseBotInstance
from .models import Message, ChannelType, MessageType
from .helpers import timestamp_date_time, get_now_date_time
from .telegram import BaseNickStore, MemNickStore
from .config import config


TeleMessage = namedtuple(
    'TeleMessage',
    ('msg_id', 'user_id', 'username', 'chat_id', 'content', 'mtype', 'ts',)
)


class TgTelegram(BaseBotInstance):

    ChanTag = ChannelType.Telegram

    def __init__(self, ip_addr='127.0.0.1', port='4444', nick_store=None):
        self._socket_init(ip_addr, port)
        self.main_session()
        if not isinstance(nick_store, BaseNickStore):
            raise Exception("Invalid Nickname storage")
        self.nick_store = nick_store

    def __del__(self):
        self.sock.close()

    def _socket_init(self, ip_addr, port):
        s = socket(AF_INET, SOCK_STREAM)
        s.connect((ip_addr, port))
        self.sock = s

    def _send_cmd(self, cmd):
        if '\n' != cmd[-1]:
            cmd += '\n'
        self.sock.send(cmd.encode())

    def main_session(self):
        self._send_cmd('main_session')

    def parse_msg(self, jmsg):
        """Parse message.

        Returns:
            TeleMessage(user_id, username, chat_id, content, mtype) if jmsg is normal
            None if else.
        """
        mtype = jmsg.get('event', None)
        ts = jmsg.get('date', None)

        if mtype == "message":
            msg_id = jmsg["id"]
            from_info = jmsg["from"]
            user_id, username = from_info["id"], from_info.get("username", "")

            to_info = jmsg["to"]
            chat_id = to_info["id"] if to_info["type"] == "chat" else None

            if "text" in jmsg:
                content = jmsg["text"]
                mtype = MessageType.Command \
                    if self.is_cmd(jmsg["text"]) \
                    else MessageType.Text

                return TeleMessage(
                    msg_id=msg_id, user_id=user_id, username=username,
                    chat_id=chat_id, content=content, mtype=mtype, ts=ts,
                )

    def recv_header(self):
        """Receive and parse message head like `ANSWER XXX\n`

        Returns:
            next message size
        """

        # states = ("ANS", "NUM")
        state = "ANS"
        ans = b""
        size = b""
        while 1:
            r = self.sock.recv(1)
            if state == "ANS":
                if r == b" " and ans == b"ANSWER":
                    state = "NUM"
                else:
                    ans = ans + r
            elif state == "NUM":
                if r == b"\n":
                    break
                else:
                    size = size + r

        return int(size) + 1

    def message_stream(self, id_blacklist=None):
        """Iterator of messages.

        Yields:
            Fishroom Message instances
        """
        if isinstance(id_blacklist, (list, set, tuple)):
            id_blacklist = set(id_blacklist)
        else:
            id_blacklist = []

        while True:
            buf_size = self.recv_header()

            ret = self.sock.recv(buf_size)

            if '' == ret:
                break

            if ret[-2:] != b"\n\n":
                print("Error: buffer receive failed")
                break

            try:
                jmsg = json.loads(ret[:-2].decode("utf-8"))
            except ValueError:
                print("Error parsing: ", ret[:-2])
            # pprint.pprint(msg)
            # return self.parse_msg(jmsg)

            telemsg = self.parse_msg(jmsg)
            if (telemsg is None or
                    telemsg.chat_id is None or
                    telemsg.user_id in id_blacklist):
                continue

            nickname = self.nick_store.get_nickname(
                telemsg.user_id, telemsg.username)

            receiver = str(-telemsg.chat_id)

            date, time = timestamp_date_time(telemsg.ts) \
                if telemsg.ts else get_now_date_time()

            yield Message(
                ChannelType.Telegram, nickname, receiver,
                telemsg.content, telemsg.mtype, date=date, time=time
            )


def TgTelegramThread(tg, bus):
    tele_me = [int(x) for x in config["telegram"]["me"]]
    for msg in tg.message_stream(id_blacklist=tele_me):
        if msg.mtype == MessageType.Command:
            continue
        bus.publish(msg)


if __name__ == '__main__':
    tele = TgTelegram('127.0.0.1', 27219, nick_store=MemNickStore())
    # tele.send_msg('user#67655173', 'hello')
    for msg in tele.message_stream():
        print(msg.dumps())