"""Dynamic parameters and easy access but no IDE completion.
"""

import json
import logging
import os
import os.path


logging.basicConfig(level=logging.DEBUG)

JSON_CONFIG = """{
    "url": "https://download.data.grandlyon.com/ws/ldata/velov.stations/all.json?maxfeatures=5&start=1",
    "delay": 15,
    "format": "json",
    "maxitems": 20
}"""


class ConfigEntry:
    """Stores information about a config entry"""

    def __init__(self, value=None, env=None, doc=None, default=None):
        """Initialize parameter's informations.

        :param value: Current value of the parameter
        :param env: Name of environment variable for the parameter, defaults to None
        :param doc: Description of the parameter, defaults to None
        :param default: Default value for the parameter, defaults to None
        """
        self.value = value
        self.env = env
        self.doc = doc
        self.default = default


class Config:
    def __init__(self):
        self.parameters = {}

        # self.env_override()

    def add_parameter(self, name, env=None, doc=None, default=None):
        if name is not self.parameters.keys():
            # If no environment variable is specified, use uppercased
            # parameter name
            if env is None:
                env = name.upper()

            # Use default for parameter value at creation
            self.parameters[name] = ConfigEntry(default, env, doc, default)
        else:
            logging.error("%s is already in Config.parameters dictionnary", name)

    def env_override(self):
        for key in self.parameters.keys():
            self.parameters[key].value = os.environ.get(
                self.parameters[key].env, self.parameters[key].value
            )

    def __getattr__(self, name):
        if name in self.parameters.keys():
            return self.parameters[name].value
        else:
            logging.error("%s does not exist in Config.parameters dictionnary", name)
            raise AttributeError

    def set_value(self, name, value):
        if name in self.parameters.keys():
            self.parameters[name].value = value
        else:
            logging.error("%s does not exist in Config.parameters dictionnary", name)
            raise AttributeError

    def load_config_from_file(self, filename):
        """Loads configuration from a file, overriding existing configuration"""
        if not os.path.exists(filename):
            logging.warning("Could not load config from %s: file not found", filename)
            return
        format = "json"  # default format
        if filename.endswith(".json"):
            format = "json"
        else:
            logging.warn(
                "Unknown config file extension for %s, trying default format: %s",
                filename,
                format,
            )
        with open(filename, "r") as input:
            if format == "json":
                file_config = json.load(input)
                self.load_config(file_config)
            else:
                logging.error("Unknow format: %s", format)
                raise "Unknown format: " + format

    def load_config(self, config, prefix=""):
        """Loads a config from a json-like structure.
        TODO read :
        https://stackoverflow.com/questions/6027558/flatten-nested-dictionaries-compressing-keys?noredirect=1&lq=1

        :param config: the structure to use
        :param prefix: the key used as the prefix value for keys found
                       in config or the key itself if config is a litteral
        """
        if type(config) == dict:
            for k in config:
                key = prefix + "." + k if prefix != "" else k
                if key in self.parameters.keys():
                    self.parameters[key].value = config[k]
                else:
                    self.load_config(config[k], key)
        elif type(config) == list:
            for i, value in enumerate(config):
                key = prefix + "." + str(i) if prefix != "" else str(i)
                if key in self.parameters.keys():
                    self.parameters[key].value = value
                else:
                    self.load_config(value, key)
        else:  # config is assumed to be a litteral with an unknown key
            self.add_parameter(prefix, None, "", config)

    def print_config(self):
        for k, v in self.parameters.items():
            print(f"{k} -> {v.value}")


if __name__ == "__main__":
    config = Config()

    # filename = os.path.expandvars("$HOME/Progs/python/json/http-fetcher-config.json")
    # config.load_config_from_file(filename)

    jsonconf = json.loads(JSON_CONFIG)
    config.load_config(jsonconf)

    config.print_config()

    config.env_override()

    # Check __getattr__ works
    # We have dynamic parameters, we have an easy write
    # but no completion in IDE
    print(f"config.delay = {config.delay}")

    config.set_value("delay", 30)
    config.print_config()