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

from __future__ import absolute_import

from sys import version_info
from time import gmtime, strftime, time

from core import output
from core.config import CONFIG

from twisted.internet import reactor
from twisted.internet.task import deferLater

# --- Slack compatibility wrapper ---
if version_info[0] >= 3:
    # Python 3: slack-sdk
    from slack_sdk import WebClient as SlackClientWrapper # type: ignore
else:
    # Python 2.7: slackclient 1.x
    from slackclient import SlackClient as BaseSlackClient  # type: ignore

    class SlackResponse(object):
        """Wrap Python 2 SlackClient dict to mimic SlackResponse"""
        def __init__(self, data):
            self.data = data

    class SlackClientWrapper(object):
        def __init__(self, token):
            self._client = BaseSlackClient(token)

        def chat_postMessage(self, **kwargs):
            result = self._client.api_call("chat.postMessage", **kwargs)
            return SlackResponse(result)


class Output(output.Output):
    """
    Slack webhook output plugin.
    """

    def start(self):
        self.slack_channel = CONFIG.get('output_slack', 'channel')
        self.slack_token = CONFIG.get('output_slack', 'token')
        self.delay = CONFIG.getfloat('output_slack', 'delay', fallback=1.2)
        self.sc = SlackClientWrapper(self.slack_token)
        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:
            # Re-schedule after the remainder of the delay window
            deferLater(reactor, self.delay - elapsed, self._drain)
            return
        self.last_sent = now
        message = self.requests_list.pop(0)
        self.sc.chat_postMessage(channel=self.slack_channel, text=message)
        if self.requests_list:
            deferLater(reactor, self.delay, self._drain)
