# Simple Telegram Bot logger

from json import loads
from time import gmtime, strftime, time

from core import output
from core.config import CONFIG
from core.tools import decode, encode

try:
    from urllib import quote_plus
except ImportError:
    from urllib.parse import quote_plus

from twisted.python.log import msg
from twisted.internet import reactor
from twisted.internet.task import deferLater
from twisted.web.client import Agent, HTTPConnectionPool, _HTTP11ClientFactory, readBody
from twisted.web.http_headers import Headers
from twisted.internet.ssl import ClientContextFactory


class WebClientContextFactory(ClientContextFactory):

    def getContext(self, hostname, port):
        return ClientContextFactory.getContext(self)


class QuietHTTP11ClientFactory(_HTTP11ClientFactory):
    noisy = False


class Output(output.Output):
    """
    telegram output
    """

    def start(self):
        self.bot_token = CONFIG.get('output_telegram', 'bot_token')
        self.chat_id = CONFIG.get('output_telegram', 'chat_id')
        self.delay = CONFIG.getfloat('output_telegram', 'delay', fallback=2.0)

        contextFactory = WebClientContextFactory()
        myQuietPool = HTTPConnectionPool(reactor)
        myQuietPool._factory = QuietHTTP11ClientFactory
        self.agent = Agent(reactor, contextFactory=contextFactory, pool=myQuietPool)
        self.last_sent = 0
        self.requests_list = []

    def stop(self):
        pass

    def write(self, event):
        messg = '[{} UTC] [SIPHoney on {}]: {} from {}:{}.'.format(
            strftime('%Y-%m-%d %H:%M:%S', gmtime(event['unixtime'])),
            event['sensor'], event['message'], event['src_ip'], event['dst_port']
        )
        self.send_message(messg)

    def send_message(self, message):
        def cbBody(body):
            return processResult(body)

        def cbPartial(failure):
            """
            Google HTTP Server does not set Content-Length. Twisted marks it as partial
            """
            failure.printTraceback()
            return processResult(failure.value)

        def cbResponse(response):
            if response.code in [200, 201]:
                return
            else:
                msg('Telegram error: {} {}'.format(response.code, decode(response.phrase)))
                d = readBody(response)
                d.addCallback(cbBody)
                d.addErrback(cbPartial)
                return d

        def cbError(failure):
            failure.printTraceback()

        def processResult(result):
            j = loads(result)
            msg('Telegram response: {}'.format(j))

        self.requests_list.append(message)
        now = time()
        if now - self.last_sent < self.delay:
            deferLater(reactor, self.delay, self.send_message, message)
            return
        self.last_sent = now
        message = self.requests_list.pop(0)
        headers = Headers({
            b'User-Agent': [b'SIPHoney']
        })
        params = 'chat_id={}&text={}'.format(self.chat_id, quote_plus(message))
        url = 'https://api.telegram.org/bot{}/sendMessage?{}'.format(self.bot_token, params)
        d = self.agent.request(b'GET', encode(url), headers, None)

        d.addCallback(cbResponse)
        d.addErrback(cbError)
        return d
