import asyncio
import logging
from datetime import date, datetime

from telegram import Update
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
import os
import json

from cowmesh_pi_speedtest import CowmeshPiSpeedtestTester
from cowmesh_router_iperf_test import CowmeshRouterIperfTester
from cowmesh_pi_iperf_test import CowmeshPiIperfTester

PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
SECRETS_PATH = os.path.join(PROJECT_PATH, "secrets.json")
LOG_DIR_PATH = "/datadrive/apps/moonlight_analytics/results"
with open(SECRETS_PATH, 'r') as f:
    SECRETS = json.loads(f.read())

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

class MoonlightTester:

    async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        await context.bot.send_message(chat_id=update.effective_chat.id, text="This is the moonlight analytics bot for mesh network diagnostics.\n\nType /help to see available commands.", message_thread_id=update.message.message_thread_id)


    async def caps(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """
        this is just a function to test that the bot is working as expected
        """
        text_caps = ' '.join(context.args).upper()
        print("chat_id: {}".format(update.effective_chat.id))
        print("message_thread_id: {}".format(update.message.message_thread_id))
        await context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps, message_thread_id=update.message.message_thread_id)


    async def router_iperf(self, update: Update, context: ContextTypes.DEFAULT_TYPE):

        time = context.args[0] if context.args else None
        if not time:
            time = 10

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-router-iperf.txt".format(date.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=context.bot, log_location=log_location)

        if update.effective_chat.id != int(SECRETS["TELEGRAM_LOG_CHAT_ID"]):
            await log("++ can only start iperf test from Moonlight Bot group")
            return

        await log("++ starting router iperf test with {} seconds per test".format(time))
        tester = CowmeshRouterIperfTester(log=log, time=time)
        await tester.run_test()
        await tester.output_results()

        # send log file
        document = open(log_location, 'rb')
        await context.bot.send_document(update.effective_chat.id, document,
                                        message_thread_id=update.message.message_thread_id)

    async def pi_iperf(self, update: Update, context: ContextTypes.DEFAULT_TYPE):

        time = context.args[0] if context.args else None
        if not time:
            time = 10

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-pi-iperf.txt".format(date.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=context.bot, log_location=log_location)

        if update.effective_chat.id != int(SECRETS["TELEGRAM_LOG_CHAT_ID"]):
            await log("++ can only start iperf test from Moonlight Bot group")
            return

        await log("++ starting pi iperf test with {} seconds per test".format(time))
        tester = CowmeshPiIperfTester(log=log, time=time)
        await tester.run_test()
        await tester.output_results()

        # send log file
        document = open(log_location, 'rb')
        await context.bot.send_document(update.effective_chat.id, document, message_thread_id=update.message.message_thread_id)


    async def speedtest(self, update: Update, context: ContextTypes.DEFAULT_TYPE):

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-pi-speedtest.txt".format(date.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=context.bot, log_location=log_location)

        if update.effective_chat.id != int(SECRETS["TELEGRAM_LOG_CHAT_ID"]):
            await log("++ can only start test from Moonlight Bot group")
            return

        await log("++ starting pi speedtest-cli speedtest")
        tester = CowmeshPiSpeedtestTester(log=log)
        await tester.run_test()
        await tester.output_results()

        # send log file
        document = open(log_location, 'rb')
        await context.bot.send_document(update.effective_chat.id, document,
                                        message_thread_id=update.message.message_thread_id)

    async def unknown(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        await context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that, please run /help for a list of available commands.", message_thread_id=update.message.message_thread_id)


    def about_message(self):
        msg = "This is a bot designed to help measure the performance of a mesh network through active testing. " \
              "Every night the bot runs a network test using iperf to measure the connectivity between all the nodes in the mesh, " \
              "and logs the result to this channel. " \
              "Members of this channel can also initiate a new network iperf test at any time by sending a message to this channel, " \
              "with the command /iperf . " \
              "Telegram users outside of this channel cannot initiate a test, to help keep the network secure from being " \
              "overrun by malicious users. " \
              "The network test runs at night because that is when the fewest people are using the network, and so is more likely to " \
              "give consistent results with less random variability. " \
              "Please be mindful of initiating too many iperf tests during the day, as it uses a lot of network resources " \
              "to run the test and could interfere with the internet connections of people using the network. "
        return msg


    def help_message(self):
        msg = "This bot runs an iperf test every night and logs the results here. You can also initiate a new test using the command /iperf " \
              "or read a longer message explaining how this bot works by typing the command /readme."
        return msg


    async def about(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        text = self.about_message()
        await context.bot.send_message(chat_id=update.effective_chat.id, text=text, message_thread_id=update.message.message_thread_id)


    async def help_fun(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        text = self.help_message()
        await context.bot.send_message(chat_id=update.effective_chat.id, text=text, message_thread_id=update.message.message_thread_id)

    async def send_log(self, bot, log_location):
        # send log file
        chat_id = SECRETS["TELEGRAM_LOG_CHAT_ID"]
        message_thread_id = SECRETS.get("TELEGRAM_LOG_MESSAGE_THREAD_ID")
        document = open(log_location, 'rb')
        await bot.send_document(chat_id, document, message_thread_id=message_thread_id)

    async def nightly_router_iperf(self, time=10):
        token = SECRETS["TELEGRAM_TOKEN"]
        application = ApplicationBuilder().token(token).build()
        bot = application.bot

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-router-iperf.txt".format(datetime.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=bot, log_location=log_location)

        await log("☾☾ starting nightly router-to-router iperf test with {time} seconds per test".format(time=time))
        tester = CowmeshRouterIperfTester(log=log, time=time)
        await tester.run_test()
        await tester.output_results()
        await self.send_log(bot=bot, log_location=log_location)



    async def nightly_pi_iperf(self, time=10):
        token = SECRETS["TELEGRAM_TOKEN"]
        application = ApplicationBuilder().token(token).build()
        bot = application.bot

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-pi-iperf.txt".format(datetime.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=bot, log_location=log_location)

        await log("☾☾ starting nightly computer-to-computer iperf test with {} seconds per test".format(time))
        tester = CowmeshPiIperfTester(log=log, time=time)
        await tester.run_test()
        await tester.output_results()
        await self.send_log(bot=bot, log_location=log_location)


    def get_bot(self):
        token = SECRETS["TELEGRAM_TOKEN"]
        application = ApplicationBuilder().token(token).build()
        bot = application.bot
        return bot


    def get_offline_first_log_fn(self, bot, log_location):

        chat_id = SECRETS["TELEGRAM_LOG_CHAT_ID"]
        message_thread_id = SECRETS.get("TELEGRAM_LOG_MESSAGE_THREAD_ID")
        async def offline_log(msg):
            with open(log_location, 'a') as log_file:
                log_file.write(msg)

        async def log(msg):
            await offline_log(msg)
            try:
                await bot.send_message(chat_id=chat_id, text=msg, message_thread_id=message_thread_id)
            except:
                pass

        return log

    async def nightly_pi_speedtest(self):

        bot = self.get_bot()

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-pi-speedtest.txt".format(datetime.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=bot, log_location=log_location)

        await log("☾☾ starting nightly speedtest-cli speedtest")
        tester = CowmeshPiSpeedtestTester(log=log)
        await tester.run_test()
        await tester.output_results()
        await self.send_log(bot=bot, log_location=log_location)


    async def start_iperf_servers_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        await self.start_iperf_servers()

    async def start_iperf_servers(self):

        bot = self.get_bot()

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-start-iperf-servers.txt".format(datetime.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=bot, log_location=log_location)

        await log("++ ensuring that iperf servers are running")

        tester = CowmeshRouterIperfTester(log=log)
        for i in range(0, 2):
            await tester.start_iperf_servers()

        await log("++ iperf servers are now running")

    async def cleanup_iperf_servers_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        await self.cleanup_iperf_servers()

    async def cleanup_iperf_servers(self):

        bot = self.get_bot()

        log_name = "moonlight-{:%m-%d-%Y}-{:%H-%M}-cleanup-iperf-servers.txt".format(datetime.today(), datetime.now())
        log_location = os.path.join(LOG_DIR_PATH, log_name)

        log = self.get_offline_first_log_fn(bot=bot, log_location=log_location)

        tester = CowmeshRouterIperfTester(log=log)
        for i in range(0, 2):
            await tester.cleanup_iperf_servers()


    def init_bot_listener(self):
        token = SECRETS["TELEGRAM_TOKEN"]
        application = ApplicationBuilder().token(token).build()

        start_handler = CommandHandler('start', self.start)
        application.add_handler(start_handler)

        caps_handler = CommandHandler('caps', self.caps)
        application.add_handler(caps_handler)

        about_handler = CommandHandler('readme', self.about)
        application.add_handler(about_handler)

        help_handler = CommandHandler('help', self.help_fun)
        application.add_handler(help_handler)

        iperf_handler = CommandHandler('iperf', self.router_iperf)
        application.add_handler(iperf_handler)

        pi_iperf_handler = CommandHandler('pi_iperf', self.pi_iperf)
        application.add_handler(pi_iperf_handler)

        start_iperf_servers_handler = CommandHandler('start_iperf_servers', self.start_iperf_servers_command)
        application.add_handler(start_iperf_servers_handler)

        cleanup_iperf_servers_handler = CommandHandler('cleanup_iperf_servers', self.cleanup_iperf_servers_command)
        application.add_handler(cleanup_iperf_servers_handler)

        speedtest_handler = CommandHandler('speedtest', self.speedtest)
        application.add_handler(speedtest_handler)

        unknown_handler = MessageHandler(filters.COMMAND, self.unknown)
        application.add_handler(unknown_handler)

        application.run_polling()


if __name__ == '__main__':
    tester = MoonlightTester()
    tester.init_bot_listener()