"""Contains definition of various configuration related classes"""
from copy import copy

from gtransport import _utils
from gtransport._exceptions import StrictModeError


class Configuration(object):
    """A dict like configuration class"""

    __STRICT_HEADERS__ = {'service'}

    _strict = False

    _get_headers = None

    def __init__(self, provider, **cfg):
        """
        Supported optional parameters:

            1. headers: Dict[str, Any], map of headers to be forwarded

            2. strict: bool, A flag to ensure all validation failures
            leads to exceptions being thrown, defaults to False

        @param cfg: optional configurations as key-value pairs.
        """

        self._strict = bool(cfg.get('strict'))
        self._headers = cfg.get('headers') or {}
        self._normalize_headers()
        if self._strict:
            self._validate_headers()
        self._cfg = {'provider': provider}
        self._cfg.update(cfg or {})

    def _get_cfg(self, name, default=None):
        """
        Returns the value of config identified by the given name
        if it exists
        @param name: name of the configuration
        @param default: fallback value to return in case the config
        is not present
        @return: value of the config, or the default value if not
        present
        """
        return self._cfg.get(name, default)

    def __getitem__(self, key):
        """
        Convenience method to access a configuration value.
        This is what gives instances dict-like interface::
            >>> cfg = Configuration(id=7)
            >>> print cfg['id'] # prints 7
        """
        return self._get_cfg(key, None)

    def as_dict(self):
        """
        Returns a copy of the configuration as a Python dict
        @return Dict[Hashable, Any], dict of configurations
        """
        return copy(self._cfg)

    @property
    def headers(self):
        """
        Returns the dict of headers to be forwarded with
        the request

        @return Dict[Hashable, str], dict of headers
        """
        return self._headers

    @property
    def strict(self):
        """Convenience method to get silent flag value"""
        return self._strict

    def _normalize_headers(self):
        """
        Normalizes all the given headers
        """
        self._headers = dict([
            (_utils.normalize_header_name(k), v)
            for k, v in self._headers.items()
        ])

    def _validate_headers(self):
        """
        Validates the parameter `headers` to check if all the required
        headers in strict mode are present.
        @return: None
        @raise StrictModeError if any header is missing
        """
        not_found = []
        for name in self.__STRICT_HEADERS__:
            name = _utils.normalize_header_name(name)
            if name not in self._headers:
                not_found.append(name)

        if not_found:
            raise StrictModeError(
                'Required headers [{}] not found'.format(','.join(not_found))
            )

    @property
    def provider(self):
        """
        Convenience method to get the transport provider
        """
        return self['provider']
