from datetime import datetime

import pytz

from core.constants import SOFTWARE_NAME, SOFTWARE_VERSION
from core.config import SERVER_OWNER_CALLSIGN, MAX_SPOT_AGE


# Generic spot provider class. Subclasses of this query the individual APIs for data.
class SpotProvider:

    # Constructor
    def __init__(self, provider_config):
        self.name = provider_config["name"]
        self.enabled = provider_config["enabled"]
        self.last_update_time = datetime.min.replace(tzinfo=pytz.UTC)
        self.last_spot_time = datetime.min.replace(tzinfo=pytz.UTC)
        self.status = "Not Started" if self.enabled else "Disabled"
        self.spots = None

    # Set up the provider, e.g. giving it the spot list to work from
    def setup(self, spots):
        self.spots = spots

    # Start the provider. This should return immediately after spawning threads to access the remote resources
    def start(self):
        raise NotImplementedError("Subclasses must implement this method")

    # Submit a batch of spots retrieved from the provider. Only spots that are newer than the last spot retrieved
    # by this provider will be added to the spot list, to prevent duplications. Spots passing the check will also have
    # their infer_missing() method called to complete their data set. This is called by the API-querying
    # subclasses on receiving spots.
    def submit_batch(self, spots):
        for spot in spots:
            if datetime.fromtimestamp(spot.time, pytz.UTC) > self.last_spot_time:
                # Fill in any blanks
                spot.infer_missing()
                # Add to the list
                self.spots.add(spot.id, spot, expire=MAX_SPOT_AGE)
        self.last_spot_time = datetime.fromtimestamp(max(map(lambda s: s.time, spots)), pytz.UTC)

    # Submit a single spot retrieved from the provider. This will be added to the list regardless of its age. Spots
    # passing the check will also have their infer_missing() method called to complete their data set. This is called by
    # the data streaming subclasses, which can be relied upon not to re-provide old spots.
    def submit(self, spot):
        # Fill in any blanks
        spot.infer_missing()
        # Add to the list
        self.spots.add(spot.id, spot, expire=MAX_SPOT_AGE)
        self.last_spot_time = datetime.fromtimestamp(spot.time, pytz.UTC)

    # Stop any threads and prepare for application shutdown
    def stop(self):
        raise NotImplementedError("Subclasses must implement this method")