import logging
import os
import sys

import fire
from ruamel.yaml import YAML
from typing import Optional

from cfg4py import init, enable_logging, envar

enable_logging()


class Command:

    def __init__(self):
        self.resource_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'resources/'))
        self.yaml = YAML(typ='safe')  # default, if not specfied, is 'rt' (round-trip)
        self.yaml.default_flow_style = False

        with open(os.path.join(self.resource_path, 'template.yaml'), 'r') as f:
            self.templates = self.yaml.load(f)

        self.transformed = self._transform()

    def build(self, config_dir: str):
        """
        Compile configuration files into a python class file, which is used by IDE's auto-complete function

        Args:
            config_dir: The folder where your configuration files located

        Returns:

        """
        if not os.path.exists(config_dir):
            print(f"path {config_dir} not exists")
            sys.exit(-1)

        count = 0
        for f in os.listdir(config_dir):
            if f.startswith("default") or f.startswith("dev") or f.startswith("test") or f.startswith("production"):
                print(f"found {f}")
                count += 1

        if count > 0:
            print(f"{count} files found in total")
        else:
            print("the folder contains no valid configuration files")
            sys.exit(-1)

        try:
            # set server role temporarily
            os.environ["__cfg4py_server_role__"] = "DEV"

            init(config_dir)
            sys.path.insert(0, config_dir)
            # noinspection PyUnresolvedReferences
            from cfg4py_auto_gen import Config
            print(f"Config file is built with success and saved at {os.path.join(config_dir, 'cfg4py_auto_gen')}")
        except Exception as e: # pragma: no cover
            logging.exception(e)
            print("Config file built failure.")

    def _choose_dest_dir(self, dst):
        if dst is None:
            dst = input("Where should I save configuration files?\n")

        if os.path.exists(dst):
            for f in os.listdir(dst):
                if f in ['defaults.yaml', 'dev.yaml', 'test.yaml', 'production.yaml']:
                    print(f"The folder provided already contains {f}, please choose clean one.")
                    return None
            return dst
        else:
            create = input("Path not exists, create('Q' to exit)? [Y/n]")
            if create.upper() == 'Y':
                os.makedirs(dst, exist_ok=True)
                return dst
            elif create.upper() == 'Q':
                sys.exit(-1)
            else:
                return None

    def scaffold(self, dst: Optional[str]):
        """
        Creates initial configuration files based on our choices.
        Args:
            dst:

        Returns:

        """
        print("Creating a configuration boilerplate:")
        dst = self._choose_dest_dir(dst)
        while dst is None:
            dst = self._choose_dest_dir(dst)

        yaml = YAML(typ='safe')  # default, if not specfied, is 'rt' (round-trip)
        with open(os.path.join(self.resource_path, 'template.yaml'), 'r') as f:
            templates = yaml.load(f)

        print("Which flavors do you want?")
        print("-" * 20)
        prompt = """
        0  - console + rotating file logging
        10 - redis/redis-py (gh://andymccurdy/redis-py)
        11 - redis/aioredis (gh://aio-libs/aioredis)
        20 - mysql/PyMySQL (gh://PyMySQL/PyMySQL)
        30 - postgres/asyncpg (gh://MagicStack/asyncpg)
        31 - postgres/psycopg2 (gh://psycopg/psycopg2)
        40 - mq/pika (gh://pika/pika)
        50 - mongodb/pymongo (gh://mongodb/mongo-python-driver)
        """
        print(prompt)
        chooses = input("Please choose flavors by index, separated each by a comma(,):\n")
        flavors = {}
        mapping = {
            "0": "logging",
            "1": "redis",
            "2": "mysql",
            "3": "postgres",
            "4": "mq",
            "5": "mongodb"
        }
        for index in chooses.strip(' ').split(','):
            if index == "0":
                flavors["logging"] = templates["logging"]
                continue

            try:
                major = mapping[index[0]]
                minor = int(index[1])
                flavors[major] = list(templates[major][minor].values())[0]
            except (ValueError, KeyError):
                print(f"Wrong index {index}, skipped.")
                continue

        with open(os.path.join(dst, "defaults.yaml"), 'w') as f:
            f.writelines("#auto generated by Cfg4Py: https://github.com/jieyu-tech/cfg4py\n")
            yaml.dump(flavors, f)

        print(f"Cfg4Py has generated the following files under {dst}:")
        print("defaults.yaml")
        for name in ['dev.yaml', 'test.yaml', 'production.yaml']:
            with open(os.path.join(dst, name), 'w') as f:
                f.writelines("#auto generated by Cfg4Py: https://github.com/jieyu-tech/cfg4py\n")
                print(name)

        with open(os.path.join(dst, "defaults.yaml"), 'r') as f:
            print("Content in defaults.yaml")
            for line in f.readlines():
                print(line.replace('\n', ''))

    def _show_supported_config(self):
        print("Support the following configurations:")
        for key in self.templates.keys():
            item = self.templates.get(key)
            if isinstance(item, dict):
                print(f"  {key}")
            elif isinstance(item, list):
                sub_keys = []
                for sub_item in item:
                    sub_keys.append(f"{key}/{list(sub_item.keys())[0]}")
                print(f"  {key}: {', '.join(sub_keys)}")

    def _transform(self):
        transformed = {}
        for key in self.templates:
            if isinstance(self.templates[key], dict):
                transformed[key] = self.templates[key]
            elif isinstance(self.templates[key], list):
                for item in self.templates[key]:
                    item_key = list(item.keys())[0]
                    transformed[f"{key}/{item_key}"] = item

        return transformed

    def hint(self, what: str = None, usage: bool = False):
        """show a cheat sheet for configurations.
        for example:
            cfg4py hint mysql
        this will print how to configure PyMySQL
        :param what
        :param usage
        """

        if what is None or ((what not in self.templates) and what not in self.transformed):
            return self._show_supported_config()

        usage_key = f"{what}_usage"

        if usage_key in self.templates and usage:
            print("Usage:", self.templates.get(usage_key))

        if what in self.templates:
            self.yaml.dump(self.templates[what], sys.stdout)

        if usage_key in self.transformed and usage:
            print("Usage:", self.transformed.get(usage_key))

        if what in self.transformed:
            self.yaml.dump(self.transformed[what], sys.stdout)

    def set_server_role(self):
        print("please add the following line into your .bashrc:\n")
        print(f"export {envar}=DEV\n")
        print("You need to change DEV to TEST | PRODUCTION according to its actual role accordingly")

def main():
    cmd = Command() # pragma: no cover
    fire.Fire({ # pragma: no cover
        "build":           cmd.build,
        "scaffold":        cmd.scaffold,
        "hint":            cmd.hint,
        "set_server_role": cmd.set_server_role
    })


if __name__ == "__main__":
    main()
