from pathlib import Path

import pytest
from click.testing import CliRunner
from shepherd_herd.herd_cli import cli

from .conftest import extract_first_sheep
from .conftest import generate_h5_file


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_run_standard(cli_runner: CliRunner) -> None:
    res = cli_runner.invoke(cli, ["shell", "date"])
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_run_extra(cli_runner: CliRunner) -> None:
    res = cli_runner.invoke(cli, ["-v", "shell", "date"])
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_run_fail(cli_runner: CliRunner) -> None:
    res = cli_runner.invoke(cli, ["-v", "shell-command", "date"])
    assert res.exit_code != 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_run_sudo(cli_runner: CliRunner) -> None:
    res = cli_runner.invoke(
        cli,
        ["-v", "shell", "-s", "echo 'it's me: $USER"],
    )
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_run_sudo_long(cli_runner: CliRunner) -> None:
    res = cli_runner.invoke(
        cli,
        ["-v", "shell", "--sudo", "echo 'it's me: $USER"],
    )
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_provide_inventory(cli_runner: CliRunner, local_herd: Path) -> None:
    res = cli_runner.invoke(
        cli,
        ["-v", "-i", local_herd.as_posix(), "shell", "date"],
    )
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_provide_inventory_long(cli_runner: CliRunner, local_herd: Path) -> None:
    res = cli_runner.invoke(
        cli,
        ["--inventory", local_herd.as_posix(), "--verbose", "shell", "date"],
    )
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_provide_limit(cli_runner: CliRunner, local_herd: Path) -> None:
    sheep = extract_first_sheep(local_herd)
    res = cli_runner.invoke(
        cli,
        ["-i", local_herd.as_posix(), "-l", f"{sheep},", "-v", "shell", "date"],
    )
    assert res.exit_code == 0


@pytest.mark.timeout(10)
@pytest.mark.usefixtures("_herd_alive")
def test_provide_limit_long(cli_runner: CliRunner, local_herd: Path) -> None:
    sheep = extract_first_sheep(local_herd)
    res = cli_runner.invoke(
        cli,
        ["-i", local_herd.as_posix(), "--limit", f"{sheep},", "-v", "shell", "date"],
    )
    assert res.exit_code == 0


@pytest.mark.timeout(10)
def test_provide_limit_fail(cli_runner: CliRunner, local_herd: Path) -> None:
    res = cli_runner.invoke(
        cli,
        ["-v", "-i", local_herd.as_posix(), "-l", "MrMeeseeks,", "shell", "date"],
    )
    assert res.exit_code != 0


@pytest.mark.usefixtures("_herd_alive")
def test_distribute_retrieve_std(cli_runner: CliRunner, tmp_path: Path) -> None:
    test_file = generate_h5_file(tmp_path, "pytest_deploy.h5")
    elem_count1 = len(list(tmp_path.iterdir()))
    res = cli_runner.invoke(cli, ["-v", "distribute", test_file.as_posix()])
    assert res.exit_code == 0
    res = cli_runner.invoke(
        cli,
        [
            "-v",
            "retrieve",
            "-f",
            "-t",
            "-d",
            test_file.name,
            tmp_path.as_posix(),
        ],
    )
    assert res.exit_code == 0
    elem_count2 = len(list(tmp_path.iterdir()))
    # file got deleted in prev retrieve, so fail now
    res = cli_runner.invoke(
        cli,
        ["-v", "retrieve", "-s", test_file.name, tmp_path.as_posix()],
    )
    assert res.exit_code != 0
    elem_count3 = len(list(tmp_path.iterdir()))
    assert elem_count1 < elem_count2
    assert elem_count2 == elem_count3


@pytest.mark.usefixtures("_herd_alive")
def test_distribute_retrieve_etc(cli_runner: CliRunner, tmp_path: Path) -> None:
    test_file = generate_h5_file(tmp_path, "pytest_deploy.h5")
    elem_count1 = len(list(tmp_path.iterdir()))
    dir_remote = "/etc/shepherd/"
    res = cli_runner.invoke(
        cli,
        [
            "-v",
            "distribute",
            "--remote-path",
            dir_remote,
            test_file.as_posix(),
        ],
    )
    assert res.exit_code == 0
    res = cli_runner.invoke(
        cli,
        [
            "-v",
            "retrieve",
            "--force-stop",
            "--separate",
            "--delete",
            dir_remote + test_file.name,
            tmp_path.as_posix(),
        ],
    )
    assert res.exit_code == 0
    elem_count2 = len(list(tmp_path.iterdir()))
    # file got deleted in prev retrieve, so fail now
    res = cli_runner.invoke(
        cli,
        [
            "-v",
            "retrieve",
            "--timestamp",
            dir_remote + test_file.name,
            tmp_path.as_posix(),
        ],
    )
    assert res.exit_code != 0
    elem_count3 = len(list(tmp_path.iterdir()))
    assert elem_count1 < elem_count2
    assert elem_count2 == elem_count3


@pytest.mark.usefixtures("_herd_alive")
def test_distribute_retrieve_var(cli_runner: CliRunner, tmp_path: Path) -> None:
    test_file = generate_h5_file(tmp_path, "pytest_deploy.h5")
    elem_count1 = len(list(tmp_path.iterdir()))
    dir_remote = "/var/shepherd/"
    res = cli_runner.invoke(
        cli,
        ["-v", "distribute", "-r", dir_remote, test_file.as_posix()],
    )
    assert res.exit_code == 0
    res = cli_runner.invoke(
        cli,
        [
            "-v",
            "retrieve",
            "--force-stop",
            "--separate",
            "--delete",
            dir_remote + test_file.name,
            tmp_path.as_posix(),
        ],
    )
    assert res.exit_code == 0
    elem_count2 = len(list(tmp_path.iterdir()))
    # file got deleted in prev retrieve, so fail now
    res = cli_runner.invoke(
        cli,
        [
            "-v",
            "retrieve",
            "--timestamp",
            dir_remote + test_file.name,
            tmp_path.as_posix(),
        ],
    )
    assert res.exit_code != 0
    elem_count3 = len(list(tmp_path.iterdir()))
    assert elem_count1 < elem_count2
    assert elem_count2 == elem_count3


# TODO: test providing user and key filename
# TODO: test poweroff (reboot)
# TODO: test sudo
