import time
from pathlib import Path as P

from typer.testing import CliRunner

from powerconf.cli import app

from .unit_test_utils import working_directory

runner = CliRunner()


def test_command_helps():
    result = runner.invoke(app, ["render", "--help"])
    assert result.exit_code == 0
    assert (
        "Usage: root render [OPTIONS] CONFIG_FILE TEMPLATE_FILE OUTPUT" in result.stdout
    )

    result = runner.invoke(app, ["print-instances", "--help"])
    assert result.exit_code == 0
    assert "Usage: root print-instances [OPTIONS] CONFIG_FILE" in result.stdout

    result = runner.invoke(app, ["run", "--help"])
    assert result.exit_code == 0
    assert "Usage: root run [OPTIONS] TOOL CONFIG_FILE" in result.stdout


def test_run_command_basic(tmp_path):
    with working_directory(tmp_path):
        config_text = """

powerconf-run:
    acme:
        command:
            - echo "HI FROM TOOL"
    """

        config_file = tmp_path / "CONFIG.yml"
        config_file.write_text(config_text)

        result = runner.invoke(
            app, ["run", "missing", str(config_file)], env={"SHELL": "bash"}
        )

        assert result.exit_code == 1
        assert "'powerconf-run/missing'" in result.stderr
        assert "key in config instance" in result.stderr
        assert "0" in result.stderr

        result = runner.invoke(
            app,
            ["run", "acme", str(config_file)],
            env={"TERM": "dumb", "SHELL": "bash"},
        )

        if result.exit_code != 0:
            print("STDOUT:", result.stdout)
            print("STDERR:", result.stderr)
        assert result.exit_code == 0

        assert "Running:" in result.stdout
        assert "vvv\nHI FROM TOOL\n\n^^^" in result.stdout


def test_run_command_with_batch_configs(tmp_path):
    with working_directory(tmp_path):
        config_text = """
run:
    '@batch':
        - 1
        - 2

powerconf-run:
    acme:
        command:
            - touch OUTPUT-$(${/run}).txt
            - touch LOG-$(${/run}).txt
    """
        config_file = tmp_path / "CONFIG.yml"
        config_file.write_text(config_text)

        result = runner.invoke(app, ["run", "acme", str(config_file)])

        assert result.exit_code == 0

        assert P("OUTPUT-1.txt").exists()
        assert P("OUTPUT-2.txt").exists()
        assert P("LOG-1.txt").exists()
        assert P("LOG-2.txt").exists()


def test_run_command_with_config_file_to_render(tmp_path):
    with working_directory(tmp_path):
        config_text = """
run: 1
acme:
    simulation:
      output: output-$(${/run}).txt
powerconf-run:
    acme:
        template_config_file: config-acme.txt.template
        command:
            - touch OUTPUT-$(${/run}).txt
            - touch LOG-$(${/run}).txt
    """
        config_file = tmp_path / "CONFIG.yml"
        config_file.write_text(config_text)

        result = runner.invoke(app, ["run", "acme", str(config_file)])
        assert result.exit_code == 2
        assert "Invalid configuration" in result.stderr
        assert (
            "`powerconf-run/acme/template_config_file` was found but" in result.stderr
        )
        assert "`powerconf-run/acme/rendered_config_file` was not" in result.stderr

        config_text = """
run: 1
acme:
    simulation:
      output: output-$(${/run}).txt
powerconf-run:
    acme:
        rendered_config_file: config-acme.txt
        command:
            - touch OUTPUT-$(${/run}).txt
            - touch LOG-$(${/run}).txt
    """
        config_file.write_text(config_text)

        result = runner.invoke(app, ["run", "acme", str(config_file)])
        assert result.exit_code == 2
        assert "Invalid configuration" in result.stderr
        assert (
            "`powerconf-run/acme/rendered_config_file` was found but" in result.stderr
        )
        assert "`powerconf-run/acme/template_config_file` was not" in result.stderr

        config_text = """
run: 1
acme:
    simulation:
      output: output-$(${/run}).txt
powerconf-run:
    acme:
        template_config_file: config-acme.txt.template
        rendered_config_file: config-acme.txt
        command:
            - touch OUTPUT-$(${/run}).txt
            - touch LOG-$(${/run}).txt
    """
        config_file.write_text(config_text)

        template_config_text = """
output = {{acme/simulation/output}}
"""
        (tmp_path / "config-acme.txt.template").write_text(template_config_text)

        result = runner.invoke(app, ["run", "acme", str(config_file)])

        assert result.exit_code == 0

        print(list(P().glob("* ")))
        assert P("config-acme.txt").exists()
        assert (
            P("config-acme.txt").read_text()
            == """
output = output-1.txt
"""
        )


# def test_run_command_timing(tmp_path):
#     with working_directory(tmp_path):
#         config_text = """
# powerconf-run:
#     acme:
#         command:
#             - sleep 0.5
#     """

#         config_file = tmp_path / "CONFIG.yml"
#         config_file.write_text(config_text)

#         start = time.perf_counter()
#         result = runner.invoke(app, ["run", "acme", str(config_file)])
#         if result.exit_code != 0:
#             print("STDOUT:", result.stdout)
#             print("STDERR:", result.stderr)
#         assert result.exit_code == 0
#         end = time.perf_counter()
#         duration = end - start
#         assert duration > 0.5
#         assert duration < 1

#         config_text = """
# powerconf-run:
#     acme:
#         command:
#             - sleep 1
#     """

#         config_file.write_text(config_text)

#         start = time.perf_counter()
#         result = runner.invoke(app, ["run", "acme", str(config_file)])
#         end = time.perf_counter()
#         duration = end - start
#         assert duration > 1
#         assert duration < 2

#         config_text = """
# num:
#     '@batch':
#       - 1
#       - 2
# powerconf-run:
#     acme:
#         command:
#             - sleep 1
#     """

#         config_file.write_text(config_text)

#         start = time.perf_counter()
#         result = runner.invoke(app, ["run", "acme", str(config_file)])
#         assert result.exit_code == 0
#         end = time.perf_counter()
#         duration = end - start
#         # we are running each config in parallel (using multiprocessing)
#         # so the run time should be the same for two as it is for one.
#         assert duration > 1
#         assert duration < 2


def test_report_command(tmp_path):
    with working_directory(tmp_path):
        config_text = """
    num:
        '@batch':
          - 1
          - 2
    powerconf-report:
        columns:
         - title: 'num'
           value: $(${/num})
        """
        config_file = tmp_path / "CONFIG.yml"
        config_file.write_text(config_text)

        result = runner.invoke(app, ["report", str(config_file), "report.txt"])
        assert result.exit_code == 0
        assert P("report.txt").exists()
        report_text = P("report.txt").read_text()

        assert report_text == "num\n1\n2\n"


def teardown_module(module):
    if P("CONFIG.yml").exists():
        P("CONFIG.yml").unlink()
