from pathlib import Path


import os
import pistachio
import pytest
import shutil


# State -----------------------------------------------------------------------
def setup_module():
    """
    Setup the required resources neccessary to run all the tests.
    """
    Path("./tmp").mkdir()

    with open("./tmp/example.txt", "w") as fh:
        fh.write("The quick brown fox jumps over the lazy dog\n")
        fh.close()

    Path("./tmp/abc").mkdir()
    Path("./tmp/abc/xyz").mkdir()

    Path("./tmp/abc/file-1.txt").touch()
    Path("./tmp/abc/file-2.txt").symlink_to("./tmp/abc/file-1.txt")
    Path("./tmp/abc/xyz/file-3.txt").touch()
    Path("./tmp/abc/xyz/file-4.txt").touch()

    Path("./tmp/456").mkdir()
    Path("./tmp/456/789").mkdir()
    Path("./tmp/456/file-8.txt").touch()
    Path("./tmp/456/file-9.txt").symlink_to("./tmp/abc/file-1.txt")
    Path("./file-6.txt").touch()
    Path("./file-7.txt").symlink_to("./tmp/abc/file-1.txt")


def teardown_module():
    """
    Remove anything generated by testing.
    """
    shutil.rmtree("./tmp")


# Fixtures --------------------------------------------------------------------
@pytest.fixture
def tree_expected_results():
    return pistachio.Tree(
        path=f"""{os.getcwd()}/tmp/abc""",
        results=[
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/xyz""",
                exists=True,
                is_directory=True,
                is_file=False,
                is_symlink=False,
                name="xyz",
                stem="xyz",
                suffix=None
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/def""",
                exists=True,
                is_directory=True,
                is_file=False,
                is_symlink=False,
                name="def",
                stem="def",
                suffix=None
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/ghi""",
                exists=True,
                is_directory=True,
                is_file=False,
                is_symlink=False,
                name="ghi",
                stem="ghi",
                suffix=None
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/file-1.txt""",
                exists=True,
                is_directory=False,
                is_file=True,
                is_symlink=False,
                name="file-1.txt",
                stem="file-1",
                suffix="txt"
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/file-2.txt""",
                exists=True,
                is_directory=False,
                is_file=False,
                is_symlink=True,
                name="file-2.txt",
                stem="file-2",
                suffix="txt"
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/file-6.txt""",
                exists=True,
                is_directory=False,
                is_file=True,
                is_symlink=False,
                name="file-6.txt",
                stem="file-6",
                suffix="txt"
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/file-7.txt""",
                exists=True,
                is_directory=False,
                is_file=False,
                is_symlink=True,
                name="file-7.txt",
                stem="file-7",
                suffix="txt"
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/xyz/file-3.txt""",
                exists=True,
                is_directory=False,
                is_file=True,
                is_symlink=False,
                name="file-3.txt",
                stem="file-3",
                suffix="txt"
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/xyz/file-4.txt""",
                exists=True,
                is_directory=False,
                is_file=True,
                is_symlink=False,
                name="file-4.txt",
                stem="file-4",
                suffix="txt"
            ),
            pistachio.Pistachio(
                path=f"""{os.getcwd()}/tmp/abc/ghi/jkl""",
                exists=True,
                is_directory=True,
                is_file=False,
                is_symlink=False,
                name="jkl",
                stem="jkl",
                suffix=None
            )
        ]
    )


# Tests -----------------------------------------------------------------------
def test_copy_file():
    """
    Test the method to copy and paste a file on the filesystem.
    """
    result = pistachio.cp("./tmp/example.txt", "./tmp/example.rtf")

    assert result is True
    assert Path("./tmp/example.rtf").exists() is True


def test_copy_folder():
    """
    Test the method to copy and paste a file on the filesystem.
    """
    result = pistachio.cp("./tmp/abc", "./tmp/123")

    assert result is True
    assert Path("./tmp/123").exists() is True


def test_copy_symlink():
    """
    Test the method to copy and paste a file on the filesystem.
    """
    result = pistachio.cp("./tmp/abc/file-2.txt", "./tmp/123/file-5.txt")

    assert result is True
    assert Path("./tmp/123/file-5.txt").is_symlink() is True


def test_describe_schema_directory():
    """
    Test to validate the describe method response for a directory.
    """
    example = pistachio.describe("tests")

    assert isinstance(example, pistachio.Pistachio) is True


def test_describe_schema_file():
    """
    Test to validate the describe method response for a file.
    """
    example = pistachio.describe("README.md")

    assert isinstance(example, pistachio.Pistachio) is True


def test_describe_schema_symlink():
    """
    Test to validate the describe method response for a symbolic link.
    """
    example = pistachio.describe("README.rst")

    assert isinstance(example, pistachio.Pistachio) is True


def test_exists_true():
    """
    Test to confirm the exists method returns True.
    """
    assert pistachio.exists("README.md") is True


def test_exists_false():
    """
    Test to confirm the exists method returns False.
    """
    assert pistachio.exists("LICENSE.md") is False


def test_get_md5_hash():
    """
    Test to confirm the get_md5_hash method returns False.
    """
    md5_hash_str = pistachio.get_md5_hash("./tmp/example.txt")

    assert md5_hash_str == "37c4b87edffc5d198ff5a185cee7ee09"


def test_get_md5_hash_returns_none():
    """
    Test to confirm the get_md5_hash method returns None.
    """
    assert pistachio.get_md5_hash("example.docx") is None


def test_is_directory_true():
    """
    Test to confirm the is_directory method returns True.
    """
    assert pistachio.is_directory("tests") is True


def test_is_directory_false():
    """
    Test to confirm the is_directory method returns False.
    """
    assert pistachio.is_directory("LICENSE.md") is False


def test_is_file_true():
    """
    Test to confirm the is_file method returns True.
    """
    assert pistachio.is_file("README.md") is True


def test_is_file_false():
    """
    Test to confirm the is_file method returns False.
    """
    assert pistachio.is_file("tests") is False


def test_ln():
    """
    Method to verify that a symlink can be created.
    """
    link_path_str = "./tmp/shortcut.md"
    source_path_str = "../README.md"

    pistachio.ln(link_path_str, source_path_str)

    assert Path(link_path_str).exists() is True
    assert Path(link_path_str).is_symlink() is True


def test_mkdir():
    """
    Method to verify that a directory can be created.
    """
    path_str = "./tmp/abc/def"

    pistachio.mkdir(path_str)

    assert Path(path_str).exists() is True


def test_mkdir_recursively():
    """
    Method to verify that a directory path can be created recursively.
    """
    path_str = "./tmp/abc/ghi/jkl"

    pistachio.mkdir(path_str)

    assert Path(path_str).exists() is True


def test_mv_directory():
    """
    Method to confirm that a directory can be moved.
    """
    result = pistachio.mv("./tmp/456", "./tmp/123/456")

    assert result is True


def test_mv_file():
    """
    Method to confirm that a file can be moved.
    """
    result = pistachio.mv("./file-6.txt", "./tmp/abc/file-6.txt")

    assert result is True


def test_mv_symlink():
    """
    Method to confirm that a symlink can be moved.
    """
    result = pistachio.mv("./file-7.txt", "./tmp/abc/file-7.txt")

    assert result is True


def test_name_example_directory():
    """
    Test to confirm the name method returns "None".
    """
    assert pistachio.name("./foo/example") == "example"


def test_name_example_file():
    """
    Test to confirm the name method returns "README.md".
    """
    assert pistachio.name("./README.md") == "README.md"


def test_stem_example_directory():
    """
    Test to confirm the stem method returns "example".
    """
    assert pistachio.stem("./foo/example") == "example"


def test_stem_example_file():
    """
    Test to confirm the stem method returns "example".
    """
    assert pistachio.stem("./tmp/example.txt") == "example"


def test_suffix_example_directory():
    """
    Test to confirm the suffix method returns "None".
    """
    assert pistachio.suffix("./foo/example") is None


def test_suffix_example_file():
    """
    Test to confirm the suffix method returns ".txt".
    """
    assert pistachio.suffix("./tmp/example.txt") == "txt"


def test_path_builder_abs():
    """
    Method to confirm the path_builder returns an absolute path.
    """
    expected = "/tmp/foo/bar.txt"

    result = pistachio.path_builder(
        "abs",
        "/tmp",
        *[
            "./foo",
            "bar.txt"
        ]
    )

    assert result.endswith(expected)


def test_path_builder_rel():
    """
    Method to confirm the path_builder returns a relative path.
    """
    expected = "foo/bar.txt"

    result = pistachio.path_builder(
        "rel",
        "/tmp",
        *[
            "./foo",
            "bar.txt"
        ]
    )

    assert expected == result


def test_path_builder_error():
    """
    Method to confirm the path_builder returns value error.
    """
    with pytest.raises(ValueError):
        pistachio.path_builder("foo", "tmp")


def test_touch_new_file_true():
    """
    Test to confirm the touch method returns True.
    """
    assert pistachio.touch("./tmp/README.doc") is True


def test_touch_file_exist_returns_false():
    """
    Test to confirm the touch method returns True.
    """
    assert pistachio.touch("README.md") is False


def test_touch_directory_does_not_exist_returns_error():
    """
    Test to confirm the touch method raised FileNotFoundError exception.
    """
    with pytest.raises(FileNotFoundError):
        pistachio.touch("docs/README.doc")


def test_tree():
    """
    Test to confirm that the tree method returns a list of dictionaries.
    """
    assert isinstance(pistachio.tree("./tmp/abc"), pistachio.Tree) is True


def test_tree_results(tree_expected_results):
    """
    Test to confirm that the tree method returns a list of dictionaries.
    """
    assert tree_expected_results == pistachio.tree("./tmp/abc")


def test_tree_no_directory():
    """
    Test to confirm the tree method raised FileNotFoundError exception.
    """
    with pytest.raises(FileNotFoundError):
        pistachio.tree("docs")
