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",
]
"""
    )
