import os
from pathlib import Path
from unittest.mock import MagicMock, patch

from isort import exceptions, hooks


def test_git_hook(src_dir):
    """Simple smoke level testing of git hooks"""

    # Ensure correct subprocess command is called
    with patch("subprocess.run", MagicMock()) as run_mock:
        hooks.git_hook()
        run_mock.assert_called_once()
        assert run_mock.call_args[0][0] == [
            "git",
            "diff-index",
            "--cached",
            "--name-only",
            "--diff-filter=ACMRTUXB",
            "HEAD",
        ]

    with patch("subprocess.run", MagicMock()) as run_mock:
        hooks.git_hook(lazy=True)
        run_mock.assert_called_once()
        assert run_mock.call_args[0][0] == [
            "git",
            "diff-index",
            "--name-only",
            "--diff-filter=ACMRTUXB",
            "HEAD",
        ]

    # Test that non python files aren't processed
    with patch(
        "isort.hooks.get_lines",
        MagicMock(return_value=["README.md", "setup.cfg", "LICDENSE", "mkdocs.yml", "test"]),
    ):
        with patch("subprocess.run", MagicMock()) as run_mock:
            hooks.git_hook(modify=True)
            run_mock.assert_not_called()

    mock_main_py = MagicMock(return_value=[os.path.join(src_dir, "main.py")])

    mock_imperfect = MagicMock()
    mock_imperfect.return_value.stdout = b"import b\nimport a"

    # Test with incorrectly sorted file returned from git
    with patch("isort.hooks.get_lines", mock_main_py):
        with patch("subprocess.run", mock_imperfect):
            with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock:
                hooks.git_hook(modify=True)
                api_mock.assert_called_once()
                assert api_mock.call_args[0][0] == mock_main_py.return_value[0]

    # Test with sorted file returned from git and modify=False
    with patch("isort.hooks.get_lines", mock_main_py):
        with patch("subprocess.run", mock_imperfect):
            with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock:
                hooks.git_hook(modify=False)
                api_mock.assert_not_called()

    # Test with skipped file returned from git
    with patch(
        "isort.hooks.get_lines", MagicMock(return_value=[os.path.join(src_dir, "main.py")])
    ) as run_mock:

        class FakeProcessResponse:
            stdout = b"# isort: skip-file\nimport b\nimport a\n"

        with patch("subprocess.run", MagicMock(return_value=FakeProcessResponse())) as run_mock:
            with patch("isort.api", MagicMock(side_effect=exceptions.FileSkipped("", ""))):
                hooks.git_hook(modify=True)


def test_git_hook_uses_the_configuration_file_specified_in_settings_path(tmp_path: Path) -> None:
    subdirectory_path = tmp_path / "subdirectory"
    configuration_file_path = subdirectory_path / ".isort.cfg"

    # Inserting the modified file in the parent directory of the configuration file ensures that it
    # will not be found by the normal search routine
    modified_file_path = configuration_file_path.parent.parent / "somefile.py"

    # This section will be used to check that the configuration file was indeed loaded
    section = "testsection"

    os.mkdir(subdirectory_path)
    with open(configuration_file_path, "w") as fd:
        fd.write("[isort]\n")
        fd.write(f"sections={section}")

    with open(modified_file_path, "w") as fd:
        pass

    files_modified = [str(modified_file_path.absolute())]
    with patch("isort.hooks.get_lines", MagicMock(return_value=files_modified)):
        with patch("isort.hooks.get_output", MagicMock(return_value="")):
            with patch("isort.api.check_code_string", MagicMock()) as run_mock:
                hooks.git_hook(settings_file=str(configuration_file_path))

                assert run_mock.call_args[1]["config"].sections == (section,)
