import pytest

from hatch.config.constants import ConfigEnvVars


def remove_trailing_spaces(text):
    return "".join(f"{line.rstrip()}\n" for line in text.splitlines(True))


class TestErrors:
    def test_path_is_file(self, hatch, temp_dir):
        with temp_dir.as_cwd():
            path = temp_dir / "foo"
            path.touch()

            result = hatch("new", "foo")

        assert result.exit_code == 1
        assert result.output == f"Path `{path}` points to a file.\n"

    def test_path_not_empty(self, hatch, temp_dir):
        with temp_dir.as_cwd():
            path = temp_dir / "foo"
            (path / "bar").ensure_dir_exists()

            result = hatch("new", "foo")

        assert result.exit_code == 1
        assert result.output == f"Directory `{path}` is not empty.\n"

    def test_no_plugins_found(self, hatch, config_file, temp_dir):
        project_name = "My.App"
        config_file.model.template.plugins = {"foo": {}}
        config_file.save()

        with temp_dir.as_cwd():
            result = hatch("new", project_name)

        assert result.exit_code == 1
        assert result.output == "None of the defined plugins were found: foo\n"

    def test_some_not_plugins_found(self, hatch, config_file, temp_dir):
        project_name = "My.App"
        config_file.model.template.plugins["foo"] = {}
        config_file.save()

        with temp_dir.as_cwd():
            result = hatch("new", project_name)

        assert result.exit_code == 1
        assert result.output == "Some of the defined plugins were not found: foo\n"


def test_default(hatch, helpers, temp_dir):
    project_name = "My.App"

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.default", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_default_explicit_path(hatch, helpers, temp_dir):
    project_name = "My.App"

    with temp_dir.as_cwd():
        result = hatch("new", project_name, ".")

    expected_files = helpers.get_template_files("new.default", project_name)
    helpers.assert_files(temp_dir, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        src
        └── my_app
            ├── __about__.py
            └── __init__.py
        tests
        └── __init__.py
        LICENSE.txt
        README.md
        pyproject.toml
        """
    )


def test_default_empty_plugins_table(hatch, helpers, config_file, temp_dir):
    project_name = "My.App"
    config_file.model.template.plugins = {}
    config_file.save()

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.default", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


@pytest.mark.requires_internet
def test_default_no_license_cache(hatch, helpers, temp_dir):
    project_name = "My.App"
    cache_dir = temp_dir / "cache"
    cache_dir.mkdir()

    with temp_dir.as_cwd({ConfigEnvVars.CACHE: str(cache_dir)}):
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.default", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_licenses_multiple(hatch, helpers, config_file, temp_dir):
    project_name = "My.App"
    config_file.model.template.licenses.default = ["MIT", "Apache-2.0"]
    config_file.save()

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.licenses_multiple", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── LICENSES
        │   ├── Apache-2.0.txt
        │   └── MIT.txt
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── README.md
        └── pyproject.toml
        """
    )


def test_licenses_empty(hatch, helpers, config_file, temp_dir):
    project_name = "My.App"
    config_file.model.template.licenses.default = []
    config_file.save()

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.licenses_empty", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── README.md
        └── pyproject.toml
        """
    )


def test_projects_urls_space_in_label(hatch, helpers, config_file, temp_dir):
    project_name = "My.App"
    config_file.model.template.plugins["default"]["project_urls"] = {
        "Documentation": "https://github.com/{name}/{project_name_normalized}#readme",
        "Source": "https://github.com/{name}/{project_name_normalized}",
        "Bug Tracker": "https://github.com/{name}/{project_name_normalized}/issues",
    }
    config_file.save()

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.projects_urls_space_in_label", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_projects_urls_empty(hatch, helpers, config_file, temp_dir):
    project_name = "My.App"
    config_file.model.template.plugins["default"]["project_urls"] = {}
    config_file.save()

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.projects_urls_empty", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_feature_cli(hatch, helpers, temp_dir):
    project_name = "My.App"

    with temp_dir.as_cwd():
        result = hatch("new", project_name, "--cli")

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.feature_cli", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── cli
        │       │   └── __init__.py
        │       ├── __about__.py
        │       ├── __init__.py
        │       └── __main__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_feature_ci(hatch, helpers, config_file, temp_dir):
    config_file.model.template.plugins["default"]["ci"] = True
    config_file.save()

    project_name = "My.App"

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.feature_ci", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── .github
        │   └── workflows
        │       └── test.yml
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_feature_no_src_layout(hatch, helpers, config_file, temp_dir):
    config_file.model.template.plugins["default"]["src-layout"] = False
    config_file.save()

    project_name = "My.App"

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.feature_no_src_layout", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── my_app
        │   ├── __about__.py
        │   └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_feature_tests_disable(hatch, helpers, config_file, temp_dir):
    config_file.model.template.plugins["default"]["tests"] = False
    config_file.save()

    project_name = "My.App"

    with temp_dir.as_cwd():
        result = hatch("new", project_name)

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.basic", project_name)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_no_project_name_error(hatch, helpers, temp_dir):
    with temp_dir.as_cwd():
        result = hatch("new")

    assert result.exit_code == 1
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        Missing required argument for the project name, use the -i/--interactive flag.
        """
    )


def test_interactive(hatch, helpers, temp_dir):
    project_name = "My.App"
    description = "foo \u2764"

    with temp_dir.as_cwd():
        result = hatch("new", "-i", input=f"{project_name}\n{description}")

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.default", project_name, description=description)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        f"""
        Project name: {project_name}
        Description []: {description}

        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_no_project_name_enables_interactive(hatch, helpers, temp_dir):
    project_name = "My.App"
    description = "foo"

    with temp_dir.as_cwd():
        result = hatch("new", "-i", input=f"{project_name}\n{description}")

    path = temp_dir / "my-app"

    expected_files = helpers.get_template_files("new.default", project_name, description=description)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        f"""
        Project name: {project_name}
        Description []: {description}

        my-app
        ├── src
        │   └── my_app
        │       ├── __about__.py
        │       └── __init__.py
        ├── tests
        │   └── __init__.py
        ├── LICENSE.txt
        ├── README.md
        └── pyproject.toml
        """
    )


def test_initialize_fresh(hatch, helpers, temp_dir):
    project_name = "My.App"
    description = "foo"

    with temp_dir.as_cwd():
        result = hatch("new", project_name)
        assert result.exit_code == 0, result.output

    path = temp_dir / "my-app"

    project_file = path / "pyproject.toml"
    project_file.remove()
    assert not project_file.is_file()

    with path.as_cwd():
        result = hatch("new", "--init", input=f"{project_name}\n{description}")

    expected_files = helpers.get_template_files("new.default", project_name, description=description)
    helpers.assert_files(path, expected_files)

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        f"""
        Project name: {project_name}
        Description []: {description}

        Wrote: pyproject.toml
        """
    )


def test_initialize_update(hatch, helpers, temp_dir):
    project_name = "My.App"
    description = "foo"

    project_file = temp_dir / "pyproject.toml"
    project_file.write_text(
        """\
[build-system]
req = ["hatchling"]
build-backend = "build"

[project]
name = ""
description = ""
readme = "README.md"
requires-python = ">=3.8"
license = "MIT"
dynamic = ["version"]

[tool.hatch.version]
path = "o/__init__.py"
"""
    )

    with temp_dir.as_cwd():
        result = hatch("new", "--init", input=f"{project_name}\n{description}")

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        f"""
        Project name: {project_name}
        Description []: {description}

        Updated: pyproject.toml
        """
    )
    assert len(list(temp_dir.iterdir())) == 1
    assert project_file.read_text() == (
        f"""\
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "my-app"
description = "{description}"
readme = "README.md"
requires-python = ">=3.8"
license = "MIT"
dynamic = ["version"]

[tool.hatch.version]
path = "my_app/__init__.py"
"""
    )


def test_initialize_setup_cfg_only(hatch, helpers, temp_dir):
    """
    Test initializing a project with a setup.cfg file only.
    """
    setup_cfg_file = temp_dir / "setup.cfg"
    setup_cfg_file.write_text(
        """\
[metadata]
name = testapp
version = attr:testapp.__version__
description = Foo
author = U.N. Owen
author_email = void@some.where
url = https://example.com
license = MIT
"""
    )

    with temp_dir.as_cwd():
        result = hatch("new", "--init")

    assert result.exit_code == 0, result.output
    assert remove_trailing_spaces(result.output) == helpers.dedent(
        """
        Migrating project metadata from setuptools
        """
    )

    project_file = temp_dir / "pyproject.toml"
    assert project_file.read_text() == (
        """\
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "testapp"
dynamic = ["version"]
description = "Foo"
license = "MIT"
authors = [
    { name = "U.N. Owen", email = "void@some.where" },
]

[project.urls]
Homepage = "https://example.com"

[tool.hatch.version]
path = "testapp/__init__.py"

[tool.hatch.build.targets.sdist]
include = [
    "/testapp",
]
"""
    )
