"""Test hooks"""

import pytest


@pytest.mark.parametrize(
    "pre, pre_code, post, post_code",
    [
        (False, 0, False, 0),
        (True, 0, False, 0),
        (True, 5, False, 0),
        (False, 0, True, 0),
        (False, 0, True, 5),
        (True, 0, True, 0),
        (True, 5, True, 5),
    ],
    ids=[
        "no-hooks",
        "pre-success",
        "pre-fail",
        "post-success",
        "post-fail",
        "pre-post-success",
        "pre-post-fail",
    ],
)
@pytest.mark.parametrize("cmd", ["--version", "version"])
def test_hooks(runner, yadm_cmd, paths, cmd, pre, pre_code, post, post_code):
    """Test pre/post hook"""

    # generate hooks
    if pre:
        create_hook(paths, "pre_version", pre_code)
    if post:
        create_hook(paths, "post_version", post_code)

    # run yadm
    run = runner(yadm_cmd(cmd))
    # when a pre hook fails, yadm should exit with the hook's code
    assert run.code == pre_code
    assert run.err == ""

    if pre:
        assert "HOOK:pre_version" in run.out
    # if pre hook is missing or successful, yadm itself should exit 0
    if run.success:
        if post:
            assert "HOOK:post_version" in run.out
    else:
        # when a pre hook fails, yadm should not run the command
        assert "version will not be run" in run.out
        # when a pre hook fails, yadm should not run the post hook
        assert "HOOK:post_version" not in run.out


# repo fixture is needed to test the population of YADM_HOOK_WORK
@pytest.mark.usefixtures("ds1_repo_copy")
def test_hook_env(runner, yadm_cmd, paths):
    """Test hook environment"""

    # test will be done with a non existent "git" passthru command
    # which should exit with a failing code
    cmd = "passthrucmd"

    # write the hook
    hook = paths.hooks.join(f"post_{cmd}")
    hook.write("#!/bin/bash\nenv\ndeclare\n")
    hook.chmod(0o755)

    run = runner(yadm_cmd(cmd, "extra_args"))

    # expect passthru to fail
    assert run.failure
    assert f"'{cmd}' is not a git command" in run.err

    # verify hook environment
    assert "YADM_HOOK_EXIT=1\n" in run.out
    assert f"YADM_HOOK_COMMAND={cmd}\n" in run.out
    assert f"YADM_HOOK_DIR={paths.yadm}\n" in run.out
    assert f"YADM_HOOK_FULL_COMMAND={cmd} extra_args\n" in run.out
    assert f"YADM_HOOK_REPO={paths.repo}\n" in run.out
    assert f"YADM_HOOK_WORK={paths.work}\n" in run.out
    assert "YADM_ENCRYPT_INCLUDE_FILES=\n" in run.out

    # verify the hook environment contains certain exported functions
    for func in [
        "builtin_dirname",
        "relative_path",
        "unix_path",
        "mixed_path",
    ]:
        assert f"BASH_FUNC_{func}" in run.out

    # verify the hook environment contains the list of encrypted files
    script = f"""
        YADM_TEST=1 source {paths.pgm}
        YADM_HOOKS="{paths.hooks}"
        HOOK_COMMAND="{cmd}"
        ENCRYPT_INCLUDE_FILES=(a b c)
        invoke_hook "post"
    """
    run = runner(command=["bash"], inp=script)
    assert run.success
    assert run.err == ""
    assert "YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n" in run.out


def test_escaped(runner, yadm_cmd, paths):
    """Test escaped values in YADM_HOOK_FULL_COMMAND"""

    # test will be done with a non existent "git" passthru command
    # which should exit with a failing code
    cmd = "passthrucmd"

    # write the hook
    hook = paths.hooks.join(f"post_{cmd}")
    hook.write("#!/bin/bash\nenv\n")
    hook.chmod(0o755)

    run = runner(yadm_cmd(cmd, "a b", "c\td", "e\\f"))

    # expect passthru to fail
    assert run.failure

    # verify escaped values
    assert f"YADM_HOOK_FULL_COMMAND={cmd} a\\ b c\\\td e\\\\f\n" in run.out


@pytest.mark.parametrize("condition", ["exec", "no-exec", "mingw"])
def test_executable(runner, paths, condition):
    """Verify hook must be exectuable"""
    cmd = "version"
    hook = paths.hooks.join(f"pre_{cmd}")
    hook.write("#!/bin/sh\necho HOOK\n")
    hook.chmod(0o644)
    if condition == "exec":
        hook.chmod(0o755)

    mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == "mingw" else ""
    script = f"""
        YADM_TEST=1 source {paths.pgm}
        YADM_HOOKS="{paths.hooks}"
        HOOK_COMMAND="{cmd}"
        {mingw}
        invoke_hook "pre"
    """
    run = runner(command=["bash"], inp=script)

    if condition != "mingw":
        assert run.success
        assert run.err == ""
    else:
        assert run.failure
        assert "Permission denied" in run.err

    if condition == "exec":
        assert "HOOK" in run.out
    elif condition == "no-exec":
        assert "HOOK" not in run.out


def create_hook(paths, name, code):
    """Create hook"""
    hook = paths.hooks.join(name)
    hook.write("#!/bin/sh\n" f"echo HOOK:{name}\n" f"exit {code}\n")
    hook.chmod(0o755)