import json
import os
import subprocess
from datetime import datetime

import py
import pytest
from hypothesis import given
from hypothesis import strategies as st

from isort import main
from isort._version import __version__
from isort.exceptions import InvalidSettingsPath
from isort.settings import DEFAULT_CONFIG, Config
from .utils import as_stream
from io import BytesIO, TextIOWrapper
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    WrapModes: Any
else:
    from isort.wrap_modes import WrapModes


@given(
    file_name=st.text(),
    config=st.builds(Config),
    check=st.booleans(),
    ask_to_apply=st.booleans(),
    write_to_stdout=st.booleans(),
)
def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdout):
    main.sort_imports(
        file_name=file_name,
        config=config,
        check=check,
        ask_to_apply=ask_to_apply,
        write_to_stdout=write_to_stdout,
    )


def test_sort_imports(tmpdir):
    tmp_file = tmpdir.join("file.py")
    tmp_file.write("import os, sys\n")
    assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted  # type: ignore # noqa
    main.sort_imports(str(tmp_file), DEFAULT_CONFIG)
    assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted  # type: ignore # noqa

    skip_config = Config(skip=["file.py"])
    assert main.sort_imports(  # type: ignore
        str(tmp_file), config=skip_config, check=True, disregard_skip=False
    ).skipped
    assert main.sort_imports(str(tmp_file), config=skip_config, disregard_skip=False).skipped  # type: ignore # noqa


def test_sort_imports_error_handling(tmpdir, mocker, capsys):
    tmp_file = tmpdir.join("file.py")
    tmp_file.write("import os, sys\n")
    mocker.patch("isort.core.process").side_effect = IndexError("Example unhandled exception")
    with pytest.raises(IndexError):
        main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted  # type: ignore # noqa

    out, error = capsys.readouterr()
    assert "Unrecoverable exception thrown when parsing" in error


def test_parse_args():
    assert main.parse_args([]) == {}
    assert main.parse_args(["--multi-line", "1"]) == {"multi_line_output": WrapModes.VERTICAL}
    assert main.parse_args(["--multi-line", "GRID"]) == {"multi_line_output": WrapModes.GRID}
    assert main.parse_args(["--dont-order-by-type"]) == {"order_by_type": False}
    assert main.parse_args(["--dt"]) == {"order_by_type": False}
    assert main.parse_args(["--only-sections"]) == {"only_sections": True}
    assert main.parse_args(["--os"]) == {"only_sections": True}
    assert main.parse_args(["--om"]) == {"only_modified": True}
    assert main.parse_args(["--only-modified"]) == {"only_modified": True}
    assert main.parse_args(["--csi"]) == {"combine_straight_imports": True}
    assert main.parse_args(["--combine-straight-imports"]) == {"combine_straight_imports": True}
    assert main.parse_args(["--dont-follow-links"]) == {"follow_links": False}
    assert main.parse_args(["--overwrite-in-place"]) == {"overwrite_in_place": True}
    assert main.parse_args(["--from-first"]) == {"from_first": True}
    assert main.parse_args(["--resolve-all-configs"]) == {"resolve_all_configs": True}


def test_ascii_art(capsys):
    main.main(["--version"])
    out, error = capsys.readouterr()
    assert (
        out
        == f"""
                 _                 _
                (_) ___  ___  _ __| |_
                | |/ _/ / _ \\/ '__  _/
                | |\\__ \\/\\_\\/| |  | |_
                |_|\\___/\\___/\\_/   \\_/

      isort your imports, so you don't have to.

                    VERSION {__version__}

"""
    )
    assert error == ""


def test_preconvert():
    assert main._preconvert(frozenset([1, 1, 2])) == [1, 2]
    assert main._preconvert(WrapModes.GRID) == "GRID"
    assert main._preconvert(main._preconvert) == "_preconvert"
    with pytest.raises(TypeError):
        main._preconvert(datetime.now())


def test_show_files(capsys, tmpdir):
    tmpdir.join("a.py").write("import a")
    tmpdir.join("b.py").write("import b")

    # show files should list the files isort would sort
    main.main([str(tmpdir), "--show-files"])
    out, error = capsys.readouterr()
    assert "a.py" in out
    assert "b.py" in out
    assert not error

    # can not be used for stream
    with pytest.raises(SystemExit):
        main.main(["-", "--show-files"])

    # can not be used with show-config
    with pytest.raises(SystemExit):
        main.main([str(tmpdir), "--show-files", "--show-config"])


def test_missing_default_section(tmpdir):
    config_file = tmpdir.join(".isort.cfg")
    config_file.write(
        """
[settings]
sections=MADEUP
"""
    )

    python_file = tmpdir.join("file.py")
    python_file.write("import os")

    with pytest.raises(SystemExit):
        main.main([str(python_file)])


def test_ran_against_root():
    with pytest.raises(SystemExit):
        main.main(["/"])


def test_main(capsys, tmpdir):
    base_args = [
        "-sp",
        str(tmpdir),
        "--virtual-env",
        str(tmpdir),
        "--src-path",
        str(tmpdir),
    ]
    tmpdir.mkdir(".git")

    # If nothing is passed in the quick guide is returned without erroring
    main.main([])
    out, error = capsys.readouterr()
    assert main.QUICK_GUIDE in out
    assert not error

    # If no files are passed in but arguments are the quick guide is returned, alongside an error.
    with pytest.raises(SystemExit):
        main.main(base_args)
    out, error = capsys.readouterr()
    assert main.QUICK_GUIDE in out

    # Unless the config is requested, in which case it will be returned alone as JSON
    main.main(base_args + ["--show-config"])
    out, error = capsys.readouterr()
    returned_config = json.loads(out)
    assert returned_config
    assert returned_config["virtual_env"] == str(tmpdir)

    # This should work even if settings path is not provided
    main.main(base_args[2:] + ["--show-config"])
    out, error = capsys.readouterr()
    assert json.loads(out)["virtual_env"] == str(tmpdir)

    # This should raise an error if an invalid settings path is provided
    with pytest.raises(InvalidSettingsPath):
        main.main(
            base_args[2:]
            + ["--show-config"]
            + ["--settings-path", "/random-root-folder-that-cant-exist-right?"]
        )

    # Should be able to set settings path to a file
    config_file = tmpdir.join(".isort.cfg")
    config_file.write(
        """
[settings]
profile=hug
verbose=true
"""
    )
    config_args = ["--settings-path", str(config_file)]
    main.main(
        config_args
        + ["--virtual-env", "/random-root-folder-that-cant-exist-right?"]
        + ["--show-config"]
    )
    out, error = capsys.readouterr()
    assert json.loads(out)["profile"] == "hug"

    # Should be able to stream in content to sort
    input_content = TextIOWrapper(
        BytesIO(
            b"""
import b
import a
"""
        )
    )
    main.main(config_args + ["-"], stdin=input_content)
    out, error = capsys.readouterr()
    assert (
        out
        == f"""
else-type place_module for b returned {DEFAULT_CONFIG.default_section}
else-type place_module for a returned {DEFAULT_CONFIG.default_section}
import a
import b
"""
    )

    # Should be able to stream diff
    input_content = TextIOWrapper(
        BytesIO(
            b"""
import b
import a
"""
        )
    )
    main.main(config_args + ["-", "--diff"], stdin=input_content)
    out, error = capsys.readouterr()
    assert not error
    assert "+" in out
    assert "-" in out
    assert "import a" in out
    assert "import b" in out

    # check should work with stdin

    input_content_check = TextIOWrapper(
        BytesIO(
            b"""
import b
import a
"""
        )
    )

    with pytest.raises(SystemExit):
        main.main(config_args + ["-", "--check-only"], stdin=input_content_check)
    out, error = capsys.readouterr()
    assert error == "ERROR:  Imports are incorrectly sorted and/or formatted.\n"

    # Should be able to run with just a file
    python_file = tmpdir.join("has_imports.py")
    python_file.write(
        """
import b
import a
"""
    )
    main.main([str(python_file), "--filter-files", "--verbose"])
    assert python_file.read().lstrip() == "import a\nimport b\n"

    # Add a file to skip
    should_skip = tmpdir.join("should_skip.py")
    should_skip.write("import nothing")
    main.main(
        [
            str(python_file),
            str(should_skip),
            "--filter-files",
            "--verbose",
            "--skip",
            str(should_skip),
        ]
    )

    # Should raise a system exit if check only, with broken file
    python_file.write(
        """
import b
import a
"""
    )
    with pytest.raises(SystemExit):
        main.main(
            [
                str(python_file),
                str(should_skip),
                "--filter-files",
                "--verbose",
                "--check-only",
                "--skip",
                str(should_skip),
            ]
        )

    # Should have same behavior if full directory is skipped
    with pytest.raises(SystemExit):
        main.main(
            [str(tmpdir), "--filter-files", "--verbose", "--check-only", "--skip", str(should_skip)]
        )

    # Nested files should be skipped without needing --filter-files
    nested_file = tmpdir.mkdir("nested_dir").join("skip.py")
    nested_file.write("import b;import a")
    python_file.write(
        """
import a
import b
"""
    )
    main.main([str(tmpdir), "--extend-skip", "skip.py", "--check"])

    # without filter options passed in should successfully sort files
    main.main([str(python_file), str(should_skip), "--verbose", "--atomic"])

    # Should raise a system exit if all passed path is broken
    with pytest.raises(SystemExit):
        main.main(["not-exist", "--check-only"])

    # Should not raise system exit if any of passed path is not broken
    main.main([str(python_file), "not-exist", "--verbose", "--check-only"])
    out, error = capsys.readouterr()
    assert "Broken" in out

    # warnings should be displayed if old flags are used
    with pytest.warns(UserWarning):
        main.main([str(python_file), "--recursive", "-fss"])

    # warnings should be displayed when streaming input is provided with old flags as well
    with pytest.warns(UserWarning):
        main.main(["-sp", str(config_file), "-"], stdin=input_content)


def test_isort_filename_overrides(tmpdir, capsys):
    """Tests isorts available approaches for overriding filename and extension based behavior"""
    input_text = """
import b
import a

def function():
    pass
"""

    def build_input_content():
        return as_stream(input_text)

    main.main(["-"], stdin=build_input_content())
    out, error = capsys.readouterr()
    assert not error
    assert out == (
        """
import a
import b


def function():
    pass
"""
    )

    # if file is skipped it should output unchanged.
    main.main(
        ["-", "--filename", "x.py", "--skip", "x.py", "--filter-files"],
        stdin=build_input_content(),
    )
    out, error = capsys.readouterr()
    assert not error
    assert out == (
        """
import b
import a

def function():
    pass
"""
    )

    main.main(["-", "--ext-format", "pyi"], stdin=build_input_content())
    out, error = capsys.readouterr()
    assert not error
    assert out == (
        """
import a
import b

def function():
    pass
"""
    )

    tmp_file = tmpdir.join("tmp.pyi")
    tmp_file.write_text(input_text, encoding="utf8")
    main.main(["-", "--filename", str(tmp_file)], stdin=build_input_content())
    out, error = capsys.readouterr()
    assert not error
    assert out == (
        """
import a
import b

def function():
    pass
"""
    )

    # setting a filename override when file is passed in as non-stream is not supported.
    with pytest.raises(SystemExit):
        main.main([str(tmp_file), "--filename", str(tmp_file)], stdin=build_input_content())


def test_isort_float_to_top_overrides(tmpdir, capsys):
    """Tests isorts supports overriding float to top from CLI"""
    test_input = """
import b


def function():
    pass


import a
"""
    config_file = tmpdir.join(".isort.cfg")
    config_file.write(
        """
[settings]
float_to_top=True
"""
    )
    python_file = tmpdir.join("file.py")
    python_file.write(test_input)

    main.main([str(python_file)])
    out, error = capsys.readouterr()
    assert not error
    assert "Fixing" in out
    assert python_file.read_text(encoding="utf8") == (
        """
import a
import b


def function():
    pass
"""
    )

    python_file.write(test_input)
    main.main([str(python_file), "--dont-float-to-top"])
    _, error = capsys.readouterr()
    assert not error
    assert python_file.read_text(encoding="utf8") == test_input

    with pytest.raises(SystemExit):
        main.main([str(python_file), "--float-to-top", "--dont-float-to-top"])


def test_isort_with_stdin(capsys):
    # ensures that isort sorts stdin without any flags

    input_content = as_stream(
        """
import b
import a
"""
    )

    main.main(["-"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import a
import b
"""
    )

    input_content_from = as_stream(
        """
import c
import b
from a import z, y, x
"""
    )

    main.main(["-"], stdin=input_content_from)
    out, error = capsys.readouterr()

    assert out == (
        """
import b
import c
from a import x, y, z
"""
    )

    # ensures that isort correctly sorts stdin with --fas flag

    input_content = as_stream(
        """
import sys
import pandas
from z import abc
from a import xyz
"""
    )

    main.main(["-", "--fas"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from a import xyz
from z import abc

import pandas
import sys
"""
    )

    # ensures that isort correctly sorts stdin with --fass flag

    input_content = as_stream(
        """
from a import Path, abc
"""
    )

    main.main(["-", "--fass"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from a import abc, Path
"""
    )

    # ensures that isort correctly sorts stdin with --ff flag

    input_content = as_stream(
        """
import b
from c import x
from a import y
"""
    )

    main.main(["-", "--ff"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from a import y
from c import x
import b
"""
    )

    # ensures that isort correctly sorts stdin with -fss flag

    input_content = as_stream(
        """
import b
from a import a
"""
    )

    main.main(["-", "--fss"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from a import a
import b
"""
    )

    input_content = as_stream(
        """
import a
from b import c
"""
    )

    main.main(["-", "--fss"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import a
from b import c
"""
    )

    # ensures that isort correctly sorts stdin with --ds flag

    input_content = as_stream(
        """
import sys
import pandas
import a
"""
    )

    main.main(["-", "--ds"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import a
import pandas
import sys
"""
    )

    # ensures that isort correctly sorts stdin with --cs flag

    input_content = as_stream(
        """
from a import b
from a import *
"""
    )

    main.main(["-", "--cs"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from a import *
"""
    )

    # ensures that isort correctly sorts stdin with --ca flag

    input_content = as_stream(
        """
from a import x as X
from a import y as Y
"""
    )

    main.main(["-", "--ca"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from a import x as X, y as Y
"""
    )

    # ensures that isort works consistently with check and ws flags

    input_content = as_stream(
        """
import os
import a
import b
"""
    )

    main.main(["-", "--check-only", "--ws"], stdin=input_content)
    out, error = capsys.readouterr()

    assert not error

    # ensures that isort works consistently with check and diff flags

    input_content = as_stream(
        """
import b
import a
"""
    )

    with pytest.raises(SystemExit):
        main.main(["-", "--check", "--diff"], stdin=input_content)
    out, error = capsys.readouterr()

    assert error
    assert "underlying stream is not seekable" not in error
    assert "underlying stream is not seekable" not in error

    # ensures that isort correctly sorts stdin with --ls flag

    input_content = as_stream(
        """
import abcdef
import x
"""
    )

    main.main(["-", "--ls"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import x
import abcdef
"""
    )

    # ensures that isort correctly sorts stdin with --nis flag

    input_content = as_stream(
        """
from z import b, c, a
"""
    )

    main.main(["-", "--nis"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from z import b, c, a
"""
    )

    # ensures that isort correctly sorts stdin with --sl flag

    input_content = as_stream(
        """
from z import b, c, a
"""
    )

    main.main(["-", "--sl"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
from z import a
from z import b
from z import c
"""
    )

    # ensures that isort correctly sorts stdin with --top flag

    input_content = as_stream(
        """
import os
import sys
"""
    )
    main.main(["-", "--top", "sys"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import sys
import os
"""
    )

    # ensure that isort correctly sorts stdin with --os flag

    input_content = as_stream(
        """
import sys
import os
import z
from a import b, e, c
"""
    )
    main.main(["-", "--os"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import sys
import os

import z
from a import b, e, c
"""
    )

    # ensures that isort warns with deprecated flags with stdin
    input_content = as_stream(
        """
import sys
import os
"""
    )

    with pytest.warns(UserWarning):
        main.main(["-", "-ns"], stdin=input_content)

    out, error = capsys.readouterr()

    assert out == (
        """
import os
import sys
"""
    )

    input_content = as_stream(
        """
import sys
import os
"""
    )

    with pytest.warns(UserWarning):
        main.main(["-", "-k"], stdin=input_content)

    out, error = capsys.readouterr()

    assert out == (
        """
import os
import sys
"""
    )

    # ensures that only-modified flag works with stdin
    input_content = as_stream(
        """
import a
import b
"""
    )

    main.main(["-", "--verbose", "--only-modified"], stdin=input_content)
    out, error = capsys.readouterr()

    assert "else-type place_module for a returned THIRDPARTY" not in out
    assert "else-type place_module for b returned THIRDPARTY" not in out

    # ensures that combine-straight-imports flag works with stdin
    input_content = as_stream(
        """
import a
import b
"""
    )

    main.main(["-", "--combine-straight-imports"], stdin=input_content)
    out, error = capsys.readouterr()

    assert out == (
        """
import a, b
"""
    )


def test_unsupported_encodings(tmpdir, capsys):
    tmp_file = tmpdir.join("file.py")
    # fmt: off
    tmp_file.write_text(
        '''
# [syntax-error]\
# -*- coding: IBO-8859-1 -*-
""" check correct unknown encoding declaration
"""
__revision__ = 'יייי'
''',
        encoding="utf8"
    )
    # fmt: on

    # should throw an error if only unsupported encoding provided
    with pytest.raises(SystemExit):
        main.main([str(tmp_file)])
    out, error = capsys.readouterr()

    assert "No valid encodings." in error

    # should not throw an error if at least one valid encoding found
    normal_file = tmpdir.join("file1.py")
    normal_file.write("import os\nimport sys")

    main.main([str(tmp_file), str(normal_file), "--verbose"])
    out, error = capsys.readouterr()


def test_stream_skip_file(tmpdir, capsys):
    input_with_skip = """
# isort: skip_file
import b
import a
"""
    stream_with_skip = as_stream(input_with_skip)
    main.main(["-"], stdin=stream_with_skip)
    out, error = capsys.readouterr()
    assert out == input_with_skip

    input_without_skip = input_with_skip.replace("isort: skip_file", "generic comment")
    stream_without_skip = as_stream(input_without_skip)
    main.main(["-"], stdin=stream_without_skip)
    out, error = capsys.readouterr()
    assert (
        out
        == """
# generic comment
import a
import b
"""
    )

    atomic_input_without_skip = input_with_skip.replace("isort: skip_file", "generic comment")
    stream_without_skip = as_stream(atomic_input_without_skip)
    main.main(["-", "--atomic"], stdin=stream_without_skip)
    out, error = capsys.readouterr()
    assert (
        out
        == """
# generic comment
import a
import b
"""
    )


def test_only_modified_flag(tmpdir, capsys):
    # ensures there is no verbose output for correct files with only-modified flag

    file1 = tmpdir.join("file1.py")
    file1.write(
        """
import a
import b
"""
    )

    file2 = tmpdir.join("file2.py")
    file2.write(
        """
import math

import pandas as pd
"""
    )

    main.main([str(file1), str(file2), "--verbose", "--only-modified"])
    out, error = capsys.readouterr()

    assert (
        out
        == f"""
                 _                 _
                (_) ___  ___  _ __| |_
                | |/ _/ / _ \\/ '__  _/
                | |\\__ \\/\\_\\/| |  | |_
                |_|\\___/\\___/\\_/   \\_/

      isort your imports, so you don't have to.

                    VERSION {__version__}

"""
    )

    assert not error

    # ensures that verbose output is only for modified file(s) with only-modified flag

    file3 = tmpdir.join("file3.py")
    file3.write(
        """
import sys
import os
"""
    )

    main.main([str(file1), str(file2), str(file3), "--verbose", "--only-modified"])
    out, error = capsys.readouterr()

    assert "else-type place_module for sys returned STDLIB" in out
    assert "else-type place_module for os returned STDLIB" in out
    assert "else-type place_module for math returned STDLIB" not in out
    assert "else-type place_module for pandas returned THIRDPARTY" not in out

    assert not error

    # ensures that the behaviour is consistent for check flag with only-modified flag

    main.main([str(file1), str(file2), "--check-only", "--verbose", "--only-modified"])
    out, error = capsys.readouterr()

    assert (
        out
        == f"""
                 _                 _
                (_) ___  ___  _ __| |_
                | |/ _/ / _ \\/ '__  _/
                | |\\__ \\/\\_\\/| |  | |_
                |_|\\___/\\___/\\_/   \\_/

      isort your imports, so you don't have to.

                    VERSION {__version__}

"""
    )

    assert not error

    file4 = tmpdir.join("file4.py")
    file4.write(
        """
import sys
import os
"""
    )

    with pytest.raises(SystemExit):
        main.main([str(file2), str(file4), "--check-only", "--verbose", "--only-modified"])
    out, error = capsys.readouterr()

    assert "else-type place_module for sys returned STDLIB" in out
    assert "else-type place_module for os returned STDLIB" in out
    assert "else-type place_module for math returned STDLIB" not in out
    assert "else-type place_module for pandas returned THIRDPARTY" not in out


def test_identify_imports_main(tmpdir, capsys):
    file_content = "import mod2\nimport mod2\n" "a = 1\n" "import mod1\n"
    some_file = tmpdir.join("some_file.py")
    some_file.write(file_content)
    file_imports = f"{some_file}:1 import mod2\n{some_file}:4 import mod1\n"
    file_imports_with_dupes = (
        f"{some_file}:1 import mod2\n{some_file}:2 import mod2\n" f"{some_file}:4 import mod1\n"
    )

    main.identify_imports_main([str(some_file), "--unique"])
    out, error = capsys.readouterr()
    assert out.replace("\r\n", "\n") == file_imports
    assert not error

    main.identify_imports_main([str(some_file)])
    out, error = capsys.readouterr()
    assert out.replace("\r\n", "\n") == file_imports_with_dupes
    assert not error

    main.identify_imports_main(["-", "--unique"], stdin=as_stream(file_content))
    out, error = capsys.readouterr()
    assert out.replace("\r\n", "\n") == file_imports.replace(str(some_file), "")

    main.identify_imports_main(["-"], stdin=as_stream(file_content))
    out, error = capsys.readouterr()
    assert out.replace("\r\n", "\n") == file_imports_with_dupes.replace(str(some_file), "")

    main.identify_imports_main([str(tmpdir)])

    main.identify_imports_main(["-", "--packages"], stdin=as_stream(file_content))
    out, error = capsys.readouterr()
    assert len(out.split("\n")) == 6

    main.identify_imports_main(["-", "--modules"], stdin=as_stream(file_content))
    out, error = capsys.readouterr()
    assert len(out.split("\n")) == 3

    main.identify_imports_main(["-", "--attributes"], stdin=as_stream(file_content))
    out, error = capsys.readouterr()
    assert len(out.split("\n")) == 3


def test_gitignore(capsys, tmpdir: py.path.local):
    import_content = """
import b
import a
"""

    def main_check(args):
        try:
            main.main(args)
        except SystemExit:
            pass
        return capsys.readouterr()

    subprocess.run(["git", "init", str(tmpdir)])
    python_file = tmpdir.join("has_imports.py")
    python_file.write(import_content)
    tmpdir.join("no_imports.py").write("...")

    out, error = main_check([str(python_file), "--skip-gitignore", "--filter-files", "--check"])
    assert "has_imports.py" in error and "no_imports.py" not in error

    tmpdir.join(".gitignore").write("has_imports.py")

    out, error = main_check([str(python_file), "--check"])
    assert "has_imports.py" in error and "no_imports.py" not in error

    out, error = main_check([str(python_file), "--skip-gitignore", "--filter-files", "--check"])
    assert "Skipped" in out

    # Should work with nested directories
    tmpdir.mkdir("nested_dir")
    tmpdir.join(".gitignore").write("nested_dir/has_imports.py")
    subfolder_file = tmpdir.join("nested_dir/has_imports.py")
    subfolder_file.write(import_content)

    out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files", "--check"])
    assert "has_imports.py" in error and "nested_dir/has_imports.py" not in error

    # Should work with relative path
    currentdir = os.getcwd()
    os.chdir(tmpdir)

    out, error = main_check([".", "--skip-gitignore", "--filter-files", "--check"])
    assert "has_imports.py" in error and "nested_dir/has_imports.py" not in error

    tmpdir.join(".gitignore").write(
        """
nested_dir/has_imports.py
has_imports.py
"""
    )
    out, error = main_check([".", "--skip-gitignore", "--filter-files", "--check"])
    assert "Skipped" in out

    os.chdir(currentdir)

    # Should work with multiple git projects

    tmpdir.join(".git").remove()
    tmpdir.join(".gitignore").remove()

    # git_project0
    # | has_imports_ignored.py ignored
    # | has_imports.py should check
    git_project0 = tmpdir.mkdir("git_project0")
    subprocess.run(["git", "init", str(git_project0)])
    git_project0.join(".gitignore").write("has_imports_ignored.py")
    git_project0.join("has_imports_ignored.py").write(import_content)
    git_project0.join("has_imports.py").write(import_content)

    # git_project1
    # | has_imports.py should check
    # | nested_dir
    #   | has_imports_ignored.py ignored
    #   | has_imports.py should check
    # | nested_dir_ignored ignored
    #   | has_imports.py ignored from folder
    git_project1 = tmpdir.mkdir("git_project1")
    subprocess.run(["git", "init", str(git_project1)])
    git_project1.join(".gitignore").write(
        """
nested_dir/has_imports_ignored.py
nested_dir_ignored
"""
    )
    git_project1.join("has_imports.py").write(import_content)
    nested_dir = git_project1.mkdir("nested_dir")
    nested_dir.join("has_imports.py").write(import_content)
    nested_dir.join("has_imports_ignored.py").write(import_content)
    git_project1.mkdir("nested_dir_ignored").join("has_imports.py").write(import_content)

    should_check = [
        "/has_imports.py",
        "/nested_dir/has_imports.py",
        "/git_project0/has_imports.py",
        "/git_project1/has_imports.py",
        "/git_project1/nested_dir/has_imports.py",
    ]

    out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files", "--check"])

    if os.name == "nt":
        should_check = [sc.replace("/", "\\") for sc in should_check]

    assert all(f"{str(tmpdir)}{file}" in error for file in should_check)

    out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files"])

    assert all(f"{str(tmpdir)}{file}" in out for file in should_check)

    # Should work when git project contains symlinks

    if os.name != "nt":
        git_project0.join("has_imports_ignored.py").write(import_content)
        git_project0.join("has_imports.py").write(import_content)
        tmpdir.join("has_imports.py").write(import_content)
        tmpdir.join("nested_dir").join("has_imports.py").write(import_content)
        git_project0.join("ignore_link.py").mksymlinkto(tmpdir.join("has_imports.py"))
        git_project0.join("ignore_link").mksymlinkto(tmpdir.join("nested_dir"))
        git_project0.join(".gitignore").write("ignore_link.py\nignore_link", mode="a")

        out, error = main_check(
            [str(git_project0), "--skip-gitignore", "--filter-files", "--check"]
        )

        should_check = ["/git_project0/has_imports.py"]

        assert all(f"{str(tmpdir)}{file}" in error for file in should_check)

        out, error = main_check([str(git_project0), "--skip-gitignore", "--filter-files"])

        assert all(f"{str(tmpdir)}{file}" in out for file in should_check)


def test_multiple_configs(capsys, tmpdir):
    # Ensure that --resolve-all-configs flag resolves multiple configs correctly
    # and sorts files corresponding to their nearest config

    setup_cfg = """
[isort]
from_first=True
"""

    pyproject_toml = """
[tool.isort]
no_inline_sort = \"True\"
"""

    isort_cfg = """
[settings]
force_single_line=True
"""

    broken_isort_cfg = """
[iaort_confg]
force_single_line=True
"""

    dir1 = tmpdir / "subdir1"
    dir2 = tmpdir / "subdir2"
    dir3 = tmpdir / "subdir3"
    dir4 = tmpdir / "subdir4"

    dir1.mkdir()
    dir2.mkdir()
    dir3.mkdir()
    dir4.mkdir()

    setup_cfg_file = dir1 / "setup.cfg"
    setup_cfg_file.write_text(setup_cfg, "utf-8")

    pyproject_toml_file = dir2 / "pyproject.toml"
    pyproject_toml_file.write_text(pyproject_toml, "utf-8")

    isort_cfg_file = dir3 / ".isort.cfg"
    isort_cfg_file.write_text(isort_cfg, "utf-8")

    broken_isort_cfg_file = dir4 / ".isort.cfg"
    broken_isort_cfg_file.write_text(broken_isort_cfg, "utf-8")

    import_section = """
from a import y, z, x
import b
"""

    file1 = dir1 / "file1.py"
    file1.write_text(import_section, "utf-8")

    file2 = dir2 / "file2.py"
    file2.write_text(import_section, "utf-8")

    file3 = dir3 / "file3.py"
    file3.write_text(import_section, "utf-8")

    file4 = dir4 / "file4.py"
    file4.write_text(import_section, "utf-8")

    file5 = tmpdir / "file5.py"
    file5.write_text(import_section, "utf-8")

    main.main([str(tmpdir), "--resolve-all-configs", "--cr", str(tmpdir), "--verbose"])
    out, _ = capsys.readouterr()

    assert f"{str(setup_cfg_file)} used for file {str(file1)}" in out
    assert f"{str(pyproject_toml_file)} used for file {str(file2)}" in out
    assert f"{str(isort_cfg_file)} used for file {str(file3)}" in out
    assert f"default used for file {str(file4)}" in out
    assert f"default used for file {str(file5)}" in out

    assert (
        file1.read()
        == """
from a import x, y, z
import b
"""
    )

    assert (
        file2.read()
        == """
import b
from a import y, z, x
"""
    )
    assert (
        file3.read()
        == """
import b
from a import x
from a import y
from a import z
"""
    )
    assert (
        file4.read()
        == """
import b
from a import x, y, z
"""
    )

    assert (
        file5.read()
        == """
import b
from a import x, y, z
"""
    )

    # Ensure that --resolve-all-config flags works with --check

    file6 = dir1 / "file6.py"
    file6.write(
        """
import b
from a import x, y, z
    """
    )

    with pytest.raises(SystemExit):
        main.main([str(tmpdir), "--resolve-all-configs", "--cr", str(tmpdir), "--check"])

    _, err = capsys.readouterr()

    assert f"{str(file6)} Imports are incorrectly sorted and/or formatted" in err


def test_multiple_src_paths(tmpdir, capsys):
    """
    Ensure that isort has consistent behavior with multiple source paths
    """

    tests_module = tmpdir / "tests"
    app_module = tmpdir / "app"

    tests_module.mkdir()
    app_module.mkdir()

    pyproject_toml = tmpdir / "pyproject.toml"
    pyproject_toml.write_text(
        """
[tool.isort]
profile = "black"
src_paths = ["app", "tests"]
auto_identify_namespace_packages = false
""",
        "utf-8",
    )
    file = tmpdir / "file.py"
    file.write_text(
        """
from app.something import something
from tests.something import something_else
""",
        "utf-8",
    )

    for _ in range(10):  # To ensure isort has consistent results in multiple runs
        main.main([str(tmpdir), "--verbose"])
        out, _ = capsys.readouterr()

        assert (
            file.read()
            == """
from app.something import something
from tests.something import something_else
"""
        )
        assert "from-type place_module for tests.something returned FIRSTPARTY" in out
