#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Simple Telegram Bot logger

from __future__ import absolute_import

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:
    # Python 3
    from html import escape
    from urllib.parse import urlencode
except ImportError:
    # Python 2
    from cgi import escape
    from urllib import urlencode

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


class WebClientContextFactory(ClientContextFactory):

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


class QuietHTTP11ClientFactory(_HTTP11ClientFactory):
    noisy = False


class Output(output.Output):
    """
    Telegram bot output plugin.
    """

    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()
        pool = HTTPConnectionPool(reactor)
        pool._factory = QuietHTTP11ClientFactory
        self.agent = Agent(reactor, contextFactory=contextFactory, pool=pool)
        self.last_sent = 0
        self.requests_list = []

    def stop(self):
        pass

    def write(self, event):
        message = '[{} 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.requests_list.append(message)
        self._drain()

    def _drain(self):
        """Send the next queued message if the rate-limit window has elapsed.
        If messages remain, schedule another drain after self.delay seconds."""
        if not self.requests_list:
            return
        now = time()
        elapsed = now - self.last_sent
        if elapsed < self.delay:
            deferLater(reactor, self.delay - elapsed, self._drain)
            return
        self.last_sent = now
        message = self.requests_list.pop(0)
        d = self._send(message)
        if self.requests_list:
            deferLater(reactor, self.delay, self._drain)
        return d

    def _send(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.response)

        def cbResponse(response):
            if response.code in [200, 201]:
                return
            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):
            try:
                j = loads(result)
                msg('Telegram response: {}'.format(j.get('description', '')))
            except Exception:
                pass

        headers = Headers({
            b'User-Agent': [b'SIPHoney']
        })
        escaped_text = escape(message)
        params = {
            'chat_id': self.chat_id,
            'parse_mode': 'HTML',
            'text': escaped_text
        }
        url = 'https://api.telegram.org/bot{}/sendMessage?{}'.format(self.bot_token, urlencode(params))
        d = self.agent.request(b'GET', encode(url), headers, None)
        d.addCallback(cbResponse)
        d.addErrback(cbError)
        return d
