# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
"""CLI implementation for `conda export`.

Dumps specified environment package specifications to the screen.
"""

import warnings
from argparse import (
    ArgumentParser,
    Namespace,
    _SubParsersAction,
)

from conda.base.constants import KNOWN_SUBDIRS

from ..auxlib.ish import dals
from ..base.context import context
from ..common.constants import NULL
from ..models.environment import Environment
from ..plugins.environment_exporters.environment_yml import (
    ENVIRONMENT_JSON_FORMAT,
    ENVIRONMENT_YAML_FORMAT,
)


class CondaExportWarning(Warning):
    pass


def epilog() -> str:
    """Build ``conda export`` epilog (examples and plugin-driven format list)."""
    from .formats import get_available_environment_formats, get_multiplatform_lockfile

    # get environment exporters grouped by format
    formats = context.plugin_manager.get_environment_exporters_grouped()

    # find the first multiplatform lockfile
    multiplatform_lockfile = get_multiplatform_lockfile(formats)

    # compose examples/epilog
    examples = [
        "Examples:",
        "  Export an environment spec:",
        "    conda export --from-history > environment.yml",
        "",
        "  Export a lockfile for the same platform:",
        "    conda export --file explicit.txt",
    ]
    # include example for multiplatform lockfile if any are registered
    if multiplatform_lockfile:
        examples.append("")
        examples.append("  Export a lockfile for multiple platforms:")
        examples.append(
            f"    conda export --file {multiplatform_lockfile} "
            "--platform linux-64 --platform osx-arm64"
        )
    # include available formats if any are registered
    if formats:
        examples.append("")
        examples.append("Available output formats:")
        examples.append(get_available_environment_formats(formats, indent=2))
    return "\n".join(examples)


def configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:
    from .helpers import LazyChoicesAction, add_parser_json, add_parser_prefix

    summary = "Export a conda environment to a file."
    description = dals(
        """
        Export a conda environment to a file. The set of supported formats
        depends on the plugins installed in your environment. Both portable
        environment specs and reproducible lockfiles may be available. See
        the epilog for the list of formats available here.
        """
    ).rstrip()

    p = sub_parsers.add_parser(
        "export",
        help=summary,
        description=description,
        epilog=epilog(),
        **kwargs,
    )

    p.add_argument(
        "-c",
        "--channel",
        action="append",
        help="Additional channel to include in the export",
    )

    p.add_argument(
        "-O",
        "--override-channels",
        action="store_true",
        help="Do not include .condarc channels",
    )
    # NOTE: This is a different platform option from the one in helpers.py
    # This is because we want to:
    # - Allow users to specify multiple platforms for export
    # - Change the help so that it's clearer that this is for export
    #
    #  The add_parser_platform in helpers.py is used to specify a single platform/subdir
    # for the current environment.  We may want to change the helper.
    p.add_argument(
        "--platform",
        "--subdir",
        action="append",
        dest="export_platforms",
        help=(
            "Target platform(s)/subdir(s) for export (e.g. linux-64, osx-arm64, "
            "win-64). For formats that support multi-platform output, repeat "
            "the flag to produce a single file covering every platform."
        ),
    )
    p.add_argument(
        "--override-platforms",
        action="store_true",
        help="Override the platforms specified in the condarc",
    )
    add_parser_prefix(p)

    p.add_argument(
        "-f",
        "--file",
        default=None,
        required=False,
        help=(
            "File name or path for the exported environment. Standard filenames "
            "registered by the installed format plugins are auto-detected. "
            "Custom filenames require --format. Existing files are overwritten "
            "silently."
        ),
    )

    p.add_argument(
        "--format",
        default=NULL,
        required=False,
        action=LazyChoicesAction,
        choices_func=lambda: sorted(
            context.plugin_manager.get_exporter_format_mapping()
        ),
        metavar="FORMAT",
        help=(
            "Override the output format. When omitted, the format is detected "
            "from --file. See the epilog for the formats available in your "
            "installation."
        ),
    )

    p.add_argument(
        "--no-builds",
        default=False,
        action="store_true",
        required=False,
        help="Remove build specification from dependencies",
    )

    p.add_argument(
        "--ignore-channels",
        default=False,
        action="store_true",
        required=False,
        help="Do not include channel names with package names.",
    )
    add_parser_json(p)

    p.add_argument(
        "--from-history",
        default=False,
        action="store_true",
        required=False,
        help="Build environment spec from explicit specs in history",
    )
    p.set_defaults(func="conda.cli.main_export.execute")

    return p


# TODO Make this aware of channels that were used to install packages
def execute(args: Namespace, parser: ArgumentParser) -> int:
    from ..base.context import env_name
    from ..common.io import dashlist
    from ..core.prefix_data import PrefixData
    from ..exceptions import CondaValueError
    from .common import stdout_json

    unknown = set(context.export_platforms) - set(KNOWN_SUBDIRS)
    if unknown:
        raise CondaValueError(
            f"Could not find platform(s): {', '.join(sorted(unknown))}. "
            f"Valid platforms include: {', '.join(sorted(KNOWN_SUBDIRS))}"
        )

    # Early format validation - fail fast if format is unsupported
    target_format = args.format
    environment_exporter = None

    # Handle --json flag for backwards compatibility
    # If --json is specified without explicit --format AND no file, use JSON format
    # If both --json and --format are specified, --format takes precedence
    # If --json with file, --json only affects status messages
    if target_format is not NULL:
        # If explicit format provided, use it and find the appropriate exporter
        pass
    elif args.file:
        # Try to detect format by filename
        environment_exporter = context.plugin_manager.detect_environment_exporter(
            args.file
        )
        target_format = environment_exporter.name
    elif args.json:
        # Backwards compatibility: --json without --format and no file means JSON output
        target_format = ENVIRONMENT_JSON_FORMAT
    else:
        # No file and no explicit format, default to environment-yaml
        target_format = ENVIRONMENT_YAML_FORMAT

    # If no exporter was detected, try to get one by format
    if not environment_exporter:
        environment_exporter = (
            context.plugin_manager.get_environment_exporter_by_format(target_format)
        )

    # If user requested multiple platforms, we need an exporter that supports it
    if (
        len(context.export_platforms) > 1
        and not environment_exporter.multiplatform_export
    ):
        raise CondaValueError(
            f"Multiple platforms are not supported for the `{environment_exporter.name}` exporter"
        )

    prefix = context.target_prefix

    # Create models.Environment directly
    env = Environment.from_prefix(
        prefix=prefix,
        name=env_name(prefix),
        platform=context.subdir,
        from_history=args.from_history,
        no_builds=args.no_builds,
        ignore_channels=args.ignore_channels,
        channels=context.channels,
    )

    pd = PrefixData(prefix, interoperability=True)
    pypi_packages = pd.get_python_packages()
    warning: str | None = None
    if pypi_packages:
        warning = (
            "The exported environment contains 3rd party Python packages.\n\n"
            f"Your environment contains {len(pypi_packages)} package{'s' if len(pypi_packages) > 1 else ''} "
            "installed via pip. Conda cannot reliably lock these packages for reproducible "
            "environments.\n\n"
            "Detected packages:"
            f"{dashlist([package.to_simple_match_spec() for package in pypi_packages])}"
            "\n\nLearn more: https://docs.conda.io/projects/conda/en/stable/user-guide/configuration/pip-interoperability.html"
        )

    # Export using the appropriate method
    envs = [env.extrapolate(platform) for platform in context.export_platforms]
    if environment_exporter.multiplatform_export:
        exported_content = environment_exporter.multiplatform_export(envs)
    elif environment_exporter.export:
        exported_content = environment_exporter.export(envs[0])
    else:
        raise CondaValueError(
            f"No export method found for {environment_exporter.name} exporter"
        )

    # Add trailing newline to the exported content
    exported_content = exported_content.rstrip() + "\n"

    # Output the content
    if args.file:
        with open(args.file, "w") as fp:
            fp.write(exported_content)
        if args.json:
            stdout_json(
                {
                    "success": True,
                    "file": args.file,
                    "format": target_format,
                    "warnings": [warning] if warning else [],
                }
            )
        elif warning and not context.quiet:
            warnings.warn(warning, CondaExportWarning)
    else:
        if warning and not context.quiet:
            warnings.warn(warning, CondaExportWarning)
        print(exported_content, end="")

    return 0
