import os
import json
import yaml
import logging
from tickerdax.constants import NAME, Envs
from datetime import datetime, timezone
from tickerdax.client import TickerDax
from schema import Schema, And, Or, Optional
from pprint import pformat


class ConfigBase:
    """
    The base class that handles the loading and validating of the config.
    """

    def __init__(self, config=None, client_kwargs=None):
        if not client_kwargs:
            client_kwargs = {}

        self.client = TickerDax(**client_kwargs)

        self._logger = logging.getLogger(self.__class__.__name__)
        self._logger.setLevel(logging.INFO)

        self._now = datetime.now(tz=timezone.utc)
        self._config_file_path = os.environ.get(Envs.CONFIG, config)
        self._config = self._get_config_data()
        self._validate()

        self._start, self._end = self._get_time_range()

    def _validate(self):
        """
        Validates the class attributes.
        """
        timeframes = self.client.supported_timeframes
        schema = Schema({
            'routes': And(dict),
            'timeframe': And(str, lambda s: s in timeframes,
                             error=f'Your configs timeframe is not one of the supported timeframes {timeframes}'),
            'start': Or(str, datetime),
            Optional('end'): Or(str, datetime),
            Optional('back_fill'): And(bool)
        })
        schema.validate(self._config)

        available_routes = self.client.get_available_routes()
        for route in self._config.get('routes', {}).keys():
            if route not in available_routes:
                self.client.report_error(
                    f'The route "{route}" is not valid. Possible routes are: {pformat(available_routes)}'
                )

    def _get_config_data(self):
        """
        Gets the data from the config file.

        :return dict: A dictionary of config data.
        """
        if os.path.exists(self._config_file_path):
            _, extension = os.path.splitext(self._config_file_path)
            with open(self._config_file_path, 'r') as config_file:
                if extension == '.json':
                    return json.load(config_file).get(NAME, {})
                elif extension in ['.yaml', '.yml']:
                    return yaml.safe_load(config_file).get(NAME, {})
        else:
            raise self.client.report_error(f'The config file "{self._config_file_path}" does not exist on disk')

    def _get_time_range(self):
        """
        Gets the UTC start and end times for the data download.

        :return tuple(datetime, datetime):
        """
        start_string = self._config.get('start')
        end_string = self._config.get('end')

        if isinstance(start_string, str):
            start = datetime.strptime(start_string, '%Y-%m-%dT%H:%M:%S')
        else:
            start = start_string

        # if there is no ending time the current time is used
        if end_string:
            if isinstance(start_string, str):
                end = datetime.strptime(end_string, '%Y-%m-%dT%H:%M:%S')
            else:
                end = end_string
        else:
            end = self._now

        return start, end
