import copy
import importlib
import pathlib
from pathlib import Path
from typing import Annotated, List

import rich
import typer
from fspathtree import fspathtree

import powerconf

from . import loaders, rendering, utils, yaml

app = typer.Typer()
console = rich.console.Console()

__version__ = importlib.metadata.version("powerconf")
state = {"verbose": 0}


def load_config(config_file: Path):
    """load config from a file."""
    configs = []
    if not config_file.exists():
        raise typer.Exit(f"File '{config_file}' not found.")
    config_text = config_file.read_text()
    config_docs = config_text.split("---")
    for doc in config_docs:
        config = fspathtree(yaml.safe_load(doc))
        configs.append(config)

    return configs


@app.callback()
def main():
    """
    The `powerconf` command is a CLI for the powerconf python module. It allows you to read
    a configuration file, evaluate all expression, expand all batch nodes, etc, and write
    the "rendered" configurations to a file(s).
    """


@app.command()
def version():
    """
    Print the version number.
    """
    console.print(f"version: {__version__}")


@app.command()
def render(config_file: Path, template_file: Path, output: Path):
    """
    Read a powerconf-enabled configuration file and write a rendered configuration file.

    The rendered file(s) will be generated by rendering the template with each
    configuration instance as a context.
    """
    configs = yaml.powerload(config_file)

    if len(configs) == 1:
        # configuration expands into a single instance
        if output == template_file:
            raise RuntimeError(
                "Output file and template file are the same. This would overwrite the template file."
            )
        rendering.render_mustache_template_file(template_file, configs[0], output)
    else:
        output.mkdir(exist_ok=True, parents=True)
        for config in configs:
            _id = utils.get_id(config)
            output_file_suffix = template_file.suffix
            output_file_basename = template_file.with_suffix("")
            if template_file.suffix in [".template", ".tpl"]:
                output_file_suffix = output_file_basename.suffix
                output_file_basename = output_file_basename.with_suffix("")
            output_filename = output_file_basename.name + "-" + _id + output_file_suffix
            rendering.render_mustache_template_file(
                template_file, config, output / output_filename
            )


@app.command()
def print_instances(config_file: Path):
    """
    Print instances of configuration trees generated from the config.
    """
    configs = yaml.powerload(config_file)
    configs = utils.apply_transform(
        configs, lambda p, n: str(n), lambda p, n: hasattr(n, "magnitude")
    )
    print("\n---\n".join(map(lambda c: yaml.dump(c.tree), configs)))


@app.command()
def run(
    tool: Annotated[str, typer.Argument(help="The tool (i.e. model) to run.")],
    config_file: Annotated[
        pathlib.Path,
        typer.Argument(help="Confuration file describing how each model is ran."),
    ] = pathlib.Path("powerconf-run-config.yml"),
):
    run_configuration = loaders.yaml(config_file)
    # check configuration
    if "config" not in run_configuration:
        raise typer.Exit(f"No 'config' key in {config_file}.")
    model_config_file = pathlib.Path(run_configuration["config"])
    if not model_config_file.exists():
        raise typer.Exit(f"File '{model_config_file}' not found.")
    if tool not in run_configuration:
        raise typer.Exit(f"No configuration for '{tool}' in {config_file}.")

    # load all model configurations
    model_configurations = yaml.powerload(model_config_file)
    config_renderer = rendering.ConfigRenderer()
    for model_configuration in model_configurations:
        # model_configuration is a instance of the 
        run_configuration_instance = copy.deepcopy(run_configuration)
        run_configuration_instance["config"] = model_configuration.tree
        run_configuration_instance = config_renderer.render(
            run_configuration_instance[tool]
        )
        template_file = pathlib.Path(
            run_configuration_instance["run/template_file"]
        ).absolute()
        wd = run_configuration_instance["run/working_directory"]
        with utils.working_directory(wd):
            _id = utils.get_id(model_configuration)
            output_file = pathlib.Path("CONFIG-" + _id + ".txt")
            rendering.render_mustache_template_file(
                template_file, model_configuration, output_file
            )
            for cmd in run_configuration_instance["run/cmd"]:
                print(cmd)
