import logging
import yaml

from util.license import LICENSE_FILENAME, LicenseDecodeError, decode_license


logger = logging.getLogger(__name__)


class CannotWriteConfigException(Exception):
  """ Exception raised when the config cannot be written. """
  pass


class SetupIncompleteException(Exception):
  """ Exception raised when attempting to verify config that has not yet been setup. """
  pass


def import_yaml(config_obj, config_file):
  with open(config_file) as f:
    c = yaml.safe_load(f)
    if not c:
      logger.debug('Empty YAML config file')
      return

    if isinstance(c, str):
      raise Exception('Invalid YAML config file: ' + str(c))

    for key in c.iterkeys():
      if key.isupper():
        config_obj[key] = c[key]

  return config_obj


def get_yaml(config_obj):
  return yaml.safe_dump(config_obj, encoding='utf-8', allow_unicode=True)


def export_yaml(config_obj, config_file):
  try:
    with open(config_file, 'w') as f:
      f.write(get_yaml(config_obj))
  except IOError as ioe:
    raise CannotWriteConfigException(str(ioe))


class BaseProvider(object):
  """ A configuration provider helps to load, save, and handle config override in the application.
  """
  def __init__(self):
    self.license = None

  @property
  def provider_id(self):
    raise NotImplementedError

  def update_app_config(self, app_config):
    """ Updates the given application config object with the loaded override config. """
    raise NotImplementedError

  def get_config(self):
    """ Returns the contents of the config override file, or None if none. """
    raise NotImplementedError

  def save_config(self, config_object):
    """ Updates the contents of the config override file to those given. """
    raise NotImplementedError

  def config_exists(self):
    """ Returns true if a config override file exists in the config volume. """
    raise NotImplementedError

  def volume_exists(self):
    """ Returns whether the config override volume exists. """
    raise NotImplementedError

  def volume_file_exists(self, filename):
    """ Returns whether the file with the given name exists under the config override volume. """
    raise NotImplementedError

  def get_volume_file(self, filename, mode='r'):
    """ Returns a Python file referring to the given name under the config override volume. """
    raise NotImplementedError

  def write_volume_file(self, filename, contents):
    """ Writes the given contents to the config override volumne, with the given filename. """
    raise NotImplementedError

  def remove_volume_file(self, filename):
    """ Removes the config override volume file with the given filename. """
    raise NotImplementedError

  def list_volume_directory(self, path):
    """ Returns a list of strings representing the names of the files found in the config override
        directory under the given path. If the path doesn't exist, returns None.
    """
    raise NotImplementedError

  def save_volume_file(self, filename, flask_file):
    """ Saves the given flask file to the config override volume, with the given
        filename.
    """
    raise NotImplementedError

  def requires_restart(self, app_config):
    """ If true, the configuration loaded into memory for the app does not match that on disk,
        indicating that this container requires a restart.
    """
    raise NotImplementedError

  def _get_license_file(self):
    """ Returns the contents of the license file. """
    if not self.has_license_file():
     msg = 'Could not find license file. Please make sure it is in your config volume.'
     raise LicenseDecodeError(msg)

    try:
      return self.get_volume_file(LICENSE_FILENAME)
    except IOError:
      msg = 'Could not open license file. Please make sure it is in your config volume.'
      raise LicenseDecodeError(msg)

  def get_license(self):
    """ Returns the decoded license, if any. """
    with self._get_license_file() as f:
      license_file_contents = f.read()

    return decode_license(license_file_contents)

  def save_license(self, license_file_contents):
    """ Saves the given contents as the license file. """
    self.write_volume_file(LICENSE_FILENAME, license_file_contents)

  def has_license_file(self):
    """ Returns true if a license file was found in the config directory. """
    return self.volume_file_exists(LICENSE_FILENAME)