"""prosper_config.py

Unified config parsing and option picking against config objects

"""

from os import path, getenv
import configparser
from configparser import ExtendedInterpolation
import logging
import re

import anyconfig
import anytemplate

JINJA_PATTERN = re.compile(r'.*{{\S*}}.*')
def render_secrets(
        config_path,
        secret_path,
):
    """combine a jinja template with a secret .ini file

    Args:
        config_path (str): path to .cfg file with jinja templating
        secret_path (str): path to .ini-like secrets file

    Returns:
        ProsperConfig: rendered configuration object

    """
    with open(secret_path, 'r') as s_fh:
        secret_ini = anyconfig.load(s_fh, ac_parser='ini')

    with open(config_path, 'r') as c_fh:
        raw_cfg = c_fh.read()

    rendered_cfg = anytemplate.renders(raw_cfg, secret_ini, at_engine='jinja2')

    p_config = ProsperConfig(config_path)
    local_config = configparser.ConfigParser()
    local_config.optionxform = str
    local_config.read_string(rendered_cfg)

    p_config.local_config = local_config

    return p_config

def check_value(
        config,
        section,
        option,
        jinja_pattern=JINJA_PATTERN,
):
    """try to figure out if value is valid or jinja2 template value

    Args:
        config (:obj:`configparser.ConfigParser`): config object to read key from
        section (str): name of section in configparser
        option (str): name of option in configparser
        jinja_pattern (:obj:`_sre.SRE_Pattern`): a `re.compile()` pattern to match on

    Returns:
        str: value if value, else None

    Raises:
        KeyError:
        configparser.NoOptionError:
        configparser.NoSectionError:

    """
    value = config[section][option]
    if re.match(jinja_pattern, value):
        return None

    return value

class ProsperConfig(object):
    """configuration handler for all prosper projects

    Helps maintain global, local, and args values to pick according to priority

    1. args given at runtile
    2. <config_file>_local.cfg -- untracked config with #SECRETS
    3. <config_file>.cfg -- tracked 'master' config without #SECRETS
    4. environment varabile
    5. args_default -- function default w/o global config

    Args:
        config_filename (str): path to config
        local_filepath_override (str, optional): path to alternate private config file

    Attributes:
        global_config (:obj:`configparser.ConfigParser`)
        local_config (:obj:`configparser.ConfigParser`)
        config_filename (str): filename of global/tracked/default .cfg file
        local_config_filename (str): filename for local/custom .cfg file

    """
    logger = logging.getLogger('ProsperCommon')
    def __init__(
            self,
            config_filename,
            local_filepath_override='',
    ):
        self.config_filename = config_filename
        self.local_config_filename = get_local_config_filepath(config_filename)
        if local_filepath_override:
            self.local_config_filename = local_filepath_override
            #TODO: force filepaths to abspaths?
        self.global_config, self.local_config = get_configs(
            config_filename,
            self.local_config_filename,
        )

    def get(
            self,
            section_name,
            key_name,
    ):
        """Replicate configparser.get() functionality

        Args:
            section_name (str): section name in config
            key_name (str): key name in config.section_name

        Returns:
            str: do not check defaults, only return local value

        Raises:
            KeyError: unable to find option in either local or global config

        """
        value = None
        try:
            value = self.local_config.get(section_name, key_name)
        except Exception as error_msg:
            self.logger.warning(
                '%s.%s not found in local config', section_name, key_name
            )
            try:
                value = self.global_config.get(section_name, key_name)
            except Exception as error_msg:
                self.logger.error(
                    '%s.%s not found in global config', section_name, key_name
                )
                raise KeyError('Could not find option in local/global config')

        return value

    def get_option(
            self,
            section_name,
            key_name,
            args_option=None,
            args_default=None,
    ):
        """evaluates the requested option and returns the correct value

        Notes:
            Priority order
            1. args given at runtile
            2. <config_file>_local.cfg -- untracked config with #SECRETS
            3. <config_file>.cfg -- tracked 'master' config without #SECRETS
            4. environment varabile
            5. args_default -- function default w/o global config

        Args:
            section_name (str): section level name in config
            key_name (str): key name for option in config
            args_option (any): arg option given by a function
            args_default (any): arg default given by a function

        Returns:
            str: appropriate response as per priority order

        """
        if args_option != args_default and\
           args_option is not None:
            self.logger.debug('-- using function args')
            return args_option

        section_info = section_name + '.' + key_name