#!/usr/bin/env python
import pika
import json
import schedule
from threading import Thread, Event
import time
from datetime import datetime
from json import JSONDecodeError
import redis
from src.scheduler.rpc_server import RpcServer

"""
{
    "email": "anlcnydn@gmail.com",
    "subscriptions": ["anlcnydn", "alirizakeles"],
    "period": "weekly" // daily, weekly
}
"""

# Blocking connection calismazsa SelectConnection'a cevrilmeli
# connection = pika.BlockingConnection(
#     pika.ConnectionParameters(host='localhost'))


class Scheduler(RpcServer):
    def __init__(self):
        self.QUEUE = 'ab18-.rpc_queue'
        self.EXCHANGE = 'ab18-.exchange'
        self.VIRTUAL_HOST = 'ab18'
        self.CREDENTIALS = pika.PlainCredentials('ab18', '1')
        self._closing = False
        super(RpcServer, self).__init__()

    def on_request(self, ch, method, props, body):
        """
        https://www.rabbitmq.com/tutorials/tutorial-six-python.html
        """

        err = None

        try:
            body = json.loads(body)
            worker_method = getattr(self, body.get('method'))
            params = body.get('params')

            result = worker_method(**params)

            # self.schedule_the_job(params.get('period'), params.get('email'))
            # self.save_to_db(params)

        except JSONDecodeError as e:
            err_msg = "Body cannot be decoded. It may not be a valid " \
                      "json. Message: {}".format(e)
            err = {"code": -32700, "message": err_msg}
        except Exception as e:
            err_msg = "Internal Error: {}".format(e)
            err = {"code": -32700, "message": err_msg}

        response = {
            "jsonrpc": "2.0",
            "id": params.get('id'),
        }
        print(err)

        if err:
            response['error'] = err
        else:
            response['result'] = result

        ch.basic_publish(exchange='',
                         routing_key=props.reply_to,
                         properties=pika.BasicProperties(
                             correlation_id=props.correlation_id),
                         body=json.dumps(response))
        print(method.delivery_tag)
        ch.basic_ack(delivery_tag=method.delivery_tag)

    def start_consuming(self):
        """
        Overridden to use on_request callback
        Original method in rpc_server.py

        """
        self.add_on_cancel_callback()
        self._channel.basic_qos(prefetch_count=1)
        self._consumer_tag = self._channel.basic_consume(
            consumer_callback=self.on_request,
            queue=self.QUEUE,
        )
        print("Started consuming...")

    def subscribe(self, **kwargs):
        pass

    def save_to_db(self, params):
        # todo implement
        pass

    def notify_crawler(self, subscriber=None):
        if subscriber:
            # todo notify the crawler about it is the time for subscriber
            pass

    def schedule_the_job(self, period, subscriber):
        job_without_interval = schedule.every()

        if period == 'daily':
            job_with_an_interval = job_without_interval.day()
        else:
            job_with_an_interval = job_without_interval.week()

        return job_with_an_interval.do(self.notify_crawler, subscriber=subscriber)

    def run_continuously(self, interval=1):
        """
        https://raw.githubusercontent.com/mrhwick/schedule/master/schedule/__init__.py
        """
        print("Started to run continuously")
        cease_continuous_run = Event()

        class ScheduleThread(Thread):
            @classmethod
            def run(cls):
                while not cease_continuous_run.is_set():
                    schedule.run_pending()
                    time.sleep(interval)

        continuous_thread = ScheduleThread()
        continuous_thread.start()
        return cease_continuous_run


def main():
    scheduler = Scheduler()
    t1 = Thread(target=scheduler.run)
    t2 = Thread(target=scheduler.run_continuously)
    t1.start()
    t2.start()
    print("Started RpcServer...")


if __name__ == '__main__':
    main()