import json
import os

import pytest

from hatch.env.utils import get_env_var
from hatch.project.core import Project
from hatch.utils.structures import EnvVars
from hatchling.utils.constants import DEFAULT_CONFIG_FILE


@pytest.fixture(scope='module', autouse=True)
def _terminal_width():
    with EnvVars({'COLUMNS': '200'}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')]):
        yield


def test_default(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
             Standalone
        +---------+---------+
        | Name    | Type    |
        +=========+=========+
        | default | virtual |
        +---------+---------+
        """
    )


def test_default_as_json(hatch, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    with project_path.as_cwd():
        result = hatch('env', 'show', '--json')

    assert result.exit_code == 0, result.output

    environments = json.loads(result.output)
    assert list(environments) == [
        'default',
        'hatch-build',
        'hatch-static-analysis',
        'hatch-test.py3.13',
        'hatch-test.py3.12',
        'hatch-test.py3.11',
        'hatch-test.py3.10',
        'hatch-test.py3.9',
        'hatch-test.py3.8',
        'hatch-uv',
    ]
    assert environments['default'] == {'type': 'virtual'}


def test_single_only(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    project = Project(project_path)
    helpers.update_project_environment(project, 'foo', {})
    helpers.update_project_environment(project, 'bar', {})

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
             Standalone
        +---------+---------+
        | Name    | Type    |
        +=========+=========+
        | default | virtual |
        +---------+---------+
        | foo     | virtual |
        +---------+---------+
        | bar     | virtual |
        +---------+---------+
        """
    )


def test_single_and_matrix(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    project = Project(project_path)
    helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]})

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
             Standalone
        +---------+---------+
        | Name    | Type    |
        +=========+=========+
        | default | virtual |
        +---------+---------+
                     Matrices
        +------+---------+----------------+
        | Name | Type    | Envs           |
        +======+=========+================+
        | foo  | virtual | foo.py39-9000  |
        |      |         | foo.py39-3.14  |
        |      |         | foo.py310-9000 |
        |      |         | foo.py310-3.14 |
        +------+---------+----------------+
        """
    )


def test_default_matrix_only(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    project = Project(project_path)
    helpers.update_project_environment(
        project, 'default', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]}
    )

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
                     Matrices
        +---------+---------+------------+
        | Name    | Type    | Envs       |
        +=========+=========+============+
        | default | virtual | py39-9000  |
        |         |         | py39-3.14  |
        |         |         | py310-9000 |
        |         |         | py310-3.14 |
        +---------+---------+------------+
        """
    )


def test_all_matrix_types_with_single(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    project = Project(project_path)
    helpers.update_project_environment(
        project, 'default', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]}
    )
    helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]})
    helpers.update_project_environment(project, 'bar', {})

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
            Standalone
        +------+---------+
        | Name | Type    |
        +======+=========+
        | bar  | virtual |
        +------+---------+
                       Matrices
        +---------+---------+----------------+
        | Name    | Type    | Envs           |
        +=========+=========+================+
        | default | virtual | py39-9000      |
        |         |         | py39-3.14      |
        |         |         | py310-9000     |
        |         |         | py310-3.14     |
        +---------+---------+----------------+
        | foo     | virtual | foo.py39-9000  |
        |         |         | foo.py39-3.14  |
        |         |         | foo.py310-9000 |
        |         |         | foo.py310-3.14 |
        +---------+---------+----------------+
        """
    )


def test_specific(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    project = Project(project_path)
    helpers.update_project_environment(project, 'foo', {})
    helpers.update_project_environment(project, 'bar', {})

    with project_path.as_cwd():
        result = hatch('env', 'show', 'bar', 'foo', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
            Standalone
        +------+---------+
        | Name | Type    |
        +======+=========+
        | foo  | virtual |
        +------+---------+
        | bar  | virtual |
        +------+---------+
        """
    )


def test_specific_unknown(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    with project_path.as_cwd():
        result = hatch('env', 'show', 'foo', '--ascii')

    assert result.exit_code == 1, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
        Environment `foo` is not defined by project config
        """
    )


def test_optional_columns(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    dependencies = ['python___dateutil', 'bAr.Baz[TLS]   >=1.2RC5']
    extra_dependencies = ['Foo;python_version<"3.8"']
    env_vars = {'FOO': '1', 'BAR': '2'}
    description = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna \
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint \
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
"""

    project = Project(project_path)
    config = dict(project.raw_config)
    config['project']['optional-dependencies'] = {'foo_bar': [], 'baz': []}
    project.save_config(config)
    helpers.update_project_environment(
        project,
        'default',
        {
            'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}],
            'description': description,
            'dependencies': dependencies,
            'extra-dependencies': extra_dependencies,
            'env-vars': env_vars,
            'features': ['Foo...Bar', 'Baz', 'baZ'],
            'scripts': {'test': 'pytest', 'build': 'python -m build', '_foo': 'test'},
        },
    )
    helpers.update_project_environment(
        project,
        'foo',
        {
            'description': description,
            'dependencies': dependencies,
            'extra-dependencies': extra_dependencies,
            'env-vars': env_vars,
        },
    )

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
                                                                                                       Standalone
        +------+---------+----------+-----------------------------+-----------------------+---------+----------------------------------------------------------------------------------------------------------+
        | Name | Type    | Features | Dependencies                | Environment variables | Scripts | Description                                                                                              |
        +======+=========+==========+=============================+=======================+=========+==========================================================================================================+
        | foo  | virtual | baz      | bar-baz[tls]>=1.2rc5        | BAR=2                 | build   | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et   |
        |      |         | foo-bar  | foo; python_version < '3.8' | FOO=1                 | test    | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip  |
        |      |         |          | python-dateutil             |                       |         | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu |
        |      |         |          |                             |                       |         | fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia         |
        |      |         |          |                             |                       |         | deserunt mollit anim id est laborum.                                                                     |
        +------+---------+----------+-----------------------------+-----------------------+---------+----------------------------------------------------------------------------------------------------------+
                                                                                                        Matrices
        +---------+---------+------------+----------+-----------------------------+-----------------------+---------+------------------------------------------------------------------------------------------+
        | Name    | Type    | Envs       | Features | Dependencies                | Environment variables | Scripts | Description                                                                              |
        +=========+=========+============+==========+=============================+=======================+=========+==========================================================================================+
        | default | virtual | py39-9000  | baz      | bar-baz[tls]>=1.2rc5        | BAR=2                 | build   | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor           |
        |         |         | py39-3.14  | foo-bar  | foo; python_version < '3.8' | FOO=1                 | test    | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud       |
        |         |         | py310-9000 |          | python-dateutil             |                       |         | exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure    |
        |         |         | py310-3.14 |          |                             |                       |         | dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.   |
        |         |         |            |          |                             |                       |         | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt       |
        |         |         |            |          |                             |                       |         | mollit anim id est laborum.                                                              |
        +---------+---------+------------+----------+-----------------------------+-----------------------+---------+------------------------------------------------------------------------------------------+
        """
    )


def test_context_formatting(hatch, helpers, temp_dir, config_file):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    project = Project(project_path)

    # Without context formatting
    helpers.update_project_environment(
        project,
        'default',
        {
            'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}],
            'dependencies': ['foo @ {root:uri}/../foo'],
        },
    )

    # With context formatting
    helpers.update_project_environment(
        project,
        'foo',
        {
            'env-vars': {'BAR': '{env:FOO_BAZ}'},
            'dependencies': ['pydantic'],
        },
    )

    with project_path.as_cwd(env_vars={'FOO_BAZ': 'FOO_BAR'}):
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
                               Standalone
        +------+---------+--------------+-----------------------+
        | Name | Type    | Dependencies | Environment variables |
        +======+=========+==============+=======================+
        | foo  | virtual | pydantic     | BAR=FOO_BAR           |
        +------+---------+--------------+-----------------------+
                                 Matrices
        +---------+---------+------------+------------------------+
        | Name    | Type    | Envs       | Dependencies           |
        +=========+=========+============+========================+
        | default | virtual | py39-9000  | foo@ {root:uri}/../foo |
        |         |         | py39-3.14  |                        |
        |         |         | py310-9000 |                        |
        |         |         | py310-3.14 |                        |
        +---------+---------+------------+------------------------+
        """
    )


def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation):
    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)

    assert result.exit_code == 0, result.output

    project_path = temp_dir / 'my-app'
    data_path = temp_dir / 'data'
    data_path.mkdir()

    dependency = os.urandom(16).hex()
    (project_path / DEFAULT_CONFIG_FILE).write_text(
        helpers.dedent(
            f"""
            [env]
            requires = ["{dependency}"]
            """
        )
    )

    with project_path.as_cwd():
        result = hatch('env', 'show', '--ascii')

    assert result.exit_code == 0, result.output
    assert helpers.remove_trailing_spaces(result.output) == helpers.dedent(
        """
        Syncing environment plugin requirements
             Standalone
        +---------+---------+
        | Name    | Type    |
        +=========+=========+
        | default | virtual |
        +---------+---------+
        """
    )
    helpers.assert_plugin_installation(mock_plugin_installation, [dependency])
