from re import compile
from json import dumps
from time import strptime, mktime
from datetime import datetime
from lib.utils import Utils
from lib.exceptions import *
import logging

tm_logger = logging.getLogger("TweetMiner")


class TweetMiner:
    """
    The aim of this class is to split the tweet into seperate
    data fields, to form a canonical event.
    """

    def __init__(self, tweet):
        self.tweet = tweet  # {created_at: x, id: x, payload: x}

        # A typical Traffic_Mx payload is split into 3 sections:
        # "(Location) - (Reason) - (Link to further info)
        # Saves computation to split the tweet's payload once
        self.payload_location = ""
        self.payload_reason = ""
        self.payload_further_info = ""
        self.__split__()

    def __split__(self):

        if "payload" not in self.tweet:  # Prevent KeyError
            raise MissingPayloadException(self.tweet)

        payload = self.tweet["payload"]
        # Spaced to prevent splitting cities e.g. Stoke-On-Trent
        arr = payload.split(" - ")
        if len(arr) != 3:
            raise InvalidPayloadException(payload)
        self.payload_location = arr[0]
        self.payload_reason = arr[1]
        self.payload_further_info = arr[2]

    def convert_datetime_to_timeblock(self):
        """
        Typical twitter_datetime = (Wed Oct 10 19:13:35 +0000 2018)
        """
        tweet_datetime = self.tweet["created_at"]

        # Lazy check correct format
        if len(tweet_datetime.split(" ")) != 6:
            tm_logger.error('Couldnt extract datetime from {}'.format(
                tweet_datetime))
            raise DatetimeException

        time_block = {}
        time_block["time_day_worded"] = tweet_datetime.split(" ")[0]
        # Convert into python datetime to easily extract minutes, hour etc
        time_pattern = strptime(tweet_datetime, "%a %b %d %H:%M:%S +0000 %Y")
        dt = datetime.fromtimestamp(mktime(time_pattern))

        time_block["time_timestamp"] = dt.isoformat()
        time_block["time_day"] = str(dt.day)
        time_block["time_year"] = str(dt.year)
        time_block["time_hour"] = str(dt.hour)
        time_block["time_minutes"] = str(dt.minute)
        time_block["time_seconds"] = str(dt.second)

        return time_block

    def get_direction_of_incident(self):
        """
        direction: (east/north/west/south)bound
        """
        value = self.payload_location
        pattern = compile("[a-z]{4,5}bound")
        direction = pattern.search(value)

        if not direction:
            pattern = compile("clockwise")
            direction = pattern.search(value)
            if not direction:
                tm_logger.error('Couldnt extract direction from payload:{}'.format(
                value))
                raise LookupError

        direction = direction.group(0)
        return direction[0]  # The first letter suffices (nesw)

    def get_id(self):
        return self.tweet["id"]

    def get_motorway_number(self, tweet_field="screen_name"):
        """
        tweet_field = ["screen_name", payload"]
        Motorway number can be derived from the payload_location or screen_name
        """
        value = self.tweet[tweet_field]
        # Strip everything apart from a MX or MXX
        pattern = compile("[M][0-9]{1,2}")
        motorway = pattern.search(value)

        if not motorway:
            tm_logger.error('Couldnt extract motorway from {0}:{1}'.format(
                tweet_field, value))
            raise LookupError

        motorway = motorway.group(0)
        motorway_number = motorway.replace("M", "", 1)
        return motorway_number

    def get_reason_for_incident(self):
        return self.payload_reason.lower()

    def get_reported_junction(self):

        pattern = compile("[J][0-9]{1,2}")
        # Strip everything apart from a JX or JXX
        junctions = pattern.findall(self.payload_location)

        if len(junctions) < 1:
            tm_logger.error('Couldnt extract junction from payload: {}'.format(
                self.payload_location))
            raise LookupError

        for i, junc in enumerate(junctions):
            j = junc.replace("J", "")
            junctions[i] = int(j)

        return junctions

    def get_nearest_cities(self):

        nearest_cities = Utils.extract_contents_of_nested_brackets(
            self.payload_location)

        if len(nearest_cities) < 1:
            tm_logger.warn('Couldnt extract nearest_cities from payload: {}'.format(
                self.payload_location))
            nearest_cities = ['NULL']

        return nearest_cities

    def return_event_from_tweet(self):
        event = {}

        event["metadata"] = "Event Generated by Tweet Miner at {}".format(
            datetime.utcnow().isoformat())

        event["motorway"] = self.get_motorway_number()
        event["event_id"] = self.get_id()
        event["junction"] = self.get_reported_junction()
        event["direction"] = self.get_direction_of_incident()
        event["closest_cities"] = self.get_nearest_cities()
        event["reason"] = self.get_reason_for_incident()
        event["extra_information"] = []

        try:
            time_block = self.convert_datetime_to_timeblock()
        except DatetimeException:
            raise InvalidPayloadException("Probably toll")

        event.update(time_block)

        tm_logger.info("Successfully mined tweet!")
        tm_logger.debug("Event (mined tweet): {}".format(
            dumps(event, indent=4, sort_keys=True)))

        event_as_json_string = dumps(event)

        return event_as_json_string
