from __future__ import annotations

from typing import TYPE_CHECKING

import click

if TYPE_CHECKING:
    from hatch.cli.application import Application


@click.command(short_help="Show the available environments")
@click.argument("envs", required=False, nargs=-1)
@click.option("--ascii", "force_ascii", is_flag=True, help="Whether or not to only use ASCII characters")
@click.option("--json", "as_json", is_flag=True, help="Whether or not to output in JSON format")
@click.option("--internal", "-i", is_flag=True, help="Show internal environments")
@click.option("--hide-titles", is_flag=True, hidden=True)
@click.pass_obj
def show(
    app: Application,
    *,
    envs: tuple[str, ...],
    force_ascii: bool,
    as_json: bool,
    internal: bool,
    hide_titles: bool,
):
    """Show the available environments."""
    app.ensure_environment_plugin_dependencies()

    from hatch.config.constants import AppEnvVars

    if as_json:
        import json

        contextual_config = {}
        for environments in (app.project.config.envs, app.project.config.internal_envs):
            for env_name, config in environments.items():
                environment = app.project.get_environment(env_name)
                new_config = contextual_config[env_name] = dict(config)

                env_vars = dict(environment.env_vars)
                env_vars.pop(AppEnvVars.ENV_ACTIVE)
                if env_vars:
                    new_config["env-vars"] = env_vars

                num_dependencies = len(config.get("dependencies", []))
                dependencies = environment.environment_dependencies[:num_dependencies]
                if dependencies:
                    new_config["dependencies"] = dependencies

                extra_dependencies = environment.environment_dependencies[num_dependencies:]
                if extra_dependencies:
                    new_config["extra-dependencies"] = extra_dependencies

                if environment.pre_install_commands:
                    new_config["pre-install-commands"] = list(
                        environment.resolve_commands(environment.pre_install_commands)
                    )

                if environment.post_install_commands:
                    new_config["post-install-commands"] = list(
                        environment.resolve_commands(environment.post_install_commands)
                    )

                if environment.scripts:
                    new_config["scripts"] = {
                        script: list(environment.resolve_commands([script])) for script in environment.scripts
                    }

        app.display(json.dumps(contextual_config, separators=(",", ":")))
        return

    from hatch.dep.core import Dependency, InvalidDependencyError
    from hatchling.metadata.utils import get_normalized_dependency, normalize_project_name

    if internal:
        target_standalone_envs = app.project.config.internal_envs
        target_matrices = app.project.config.internal_matrices
    else:
        target_standalone_envs = app.project.config.envs
        target_matrices = app.project.config.matrices

    for env_name in envs:
        if env_name not in target_standalone_envs and env_name not in target_matrices:
            app.abort(f"Environment `{env_name}` is not defined by project config")

    env_names = set(envs)

    matrix_columns: dict[str, dict[int, str]] = {
        "Name": {},
        "Type": {},
        "Envs": {},
        "Features": {},
        "Dependencies": {},
        "Environment variables": {},
        "Scripts": {},
        "Description": {},
    }
    matrix_envs = set()
    for i, (matrix_name, matrix_data) in enumerate(target_matrices.items()):
        matrix_envs.update(matrix_data["envs"])

        if env_names and matrix_name not in env_names:
            continue

        config = matrix_data["config"]
        matrix_columns["Name"][i] = matrix_name
        matrix_columns["Type"][i] = config["type"]
        matrix_columns["Envs"][i] = "\n".join(matrix_data["envs"])

        if config.get("features"):
            if app.project.metadata.hatch.metadata.allow_ambiguous_features:
                matrix_columns["Features"][i] = "\n".join(sorted(set(config["features"])))
            else:
                matrix_columns["Features"][i] = "\n".join(
                    sorted({normalize_project_name(f) for f in config["features"]})
                )

        dependencies = []
        if config.get("dependencies"):
            dependencies.extend(config["dependencies"])
        if config.get("extra-dependencies"):
            dependencies.extend(config["extra-dependencies"])
        if dependencies:
            normalized_dependencies = set()
            for dependency in dependencies:
                try:
                    req = Dependency(dependency)
                except InvalidDependencyError:
                    normalized_dependencies.add(dependency)
                else:
                    normalized_dependencies.add(get_normalized_dependency(req))

            matrix_columns["Dependencies"][i] = "\n".join(sorted(normalized_dependencies))

        if config.get("env-vars"):
            matrix_columns["Environment variables"][i] = "\n".join(
                "=".join(item) for item in sorted(config["env-vars"].items())
            )

        if config.get("scripts"):
            matrix_columns["Scripts"][i] = "\n".join(
                sorted(script for script in config["scripts"] if app.verbose or not script.startswith("_"))
            )

        if config.get("description"):
            matrix_columns["Description"][i] = config["description"].strip()

    standalone_columns: dict[str, dict[int, str]] = {
        "Name": {},
        "Type": {},
        "Features": {},
        "Dependencies": {},
        "Environment variables": {},
        "Scripts": {},
        "Description": {},
    }
    standalone_envs = (
        (env_name, config)
        for env_name, config in target_standalone_envs.items()
        if env_names or env_name not in matrix_envs
    )
    for i, (env_name, config) in enumerate(standalone_envs):
        if env_names and env_name not in env_names:
            continue

        environment = app.project.get_environment(env_name)

        standalone_columns["Name"][i] = env_name
        standalone_columns["Type"][i] = config["type"]

        if environment.features:
            standalone_columns["Features"][i] = "\n".join(environment.features)

        if environment.environment_dependencies_complex:
            standalone_columns["Dependencies"][i] = "\n".join(
                sorted({get_normalized_dependency(d) for d in environment.environment_dependencies_complex})
            )

        env_vars = dict(environment.env_vars)
        env_vars.pop(AppEnvVars.ENV_ACTIVE)
        if env_vars:
            standalone_columns["Environment variables"][i] = "\n".join(
                "=".join(item) for item in sorted(env_vars.items())
            )

        if environment.scripts:
            standalone_columns["Scripts"][i] = "\n".join(
                sorted(script for script in environment.scripts if app.verbose or not script.startswith("_"))
            )

        if environment.description:
            standalone_columns["Description"][i] = environment.description.strip()

    column_options = {}
    for title in matrix_columns:
        if title != "Description":
            column_options[title] = {"no_wrap": True}

    app.display_table(
        "" if hide_titles else "Standalone",
        standalone_columns,
        show_lines=True,
        column_options=column_options,
        force_ascii=force_ascii,
    )
    app.display_table(
        "" if hide_titles else "Matrices",
        matrix_columns,
        show_lines=True,
        column_options=column_options,
        force_ascii=force_ascii,
    )
