#!/usr/bin/env python
# coding: utf-8 -*-
# pylint: disable=too-many-arguments
# pylint: disable=too-many-positional-arguments
# pylint: disable=line-too-long
# pylint: disable=redefined-builtin
# pylint: disable=broad-exception-caught
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# flake8: noqa E501

"""CLI commands for listing Arista package information."""

import os
from pathlib import Path
from typing import Optional

import typer

from eos_downloader.models.data import RTYPE_FEATURE
from eos_downloader.models.types import ProgressMode
from eos_downloader.logics.download import SoftManager
from eos_downloader.logics.arista_server import AristaServer
from eos_downloader.logics.arista_xml_server import EosXmlObject, CvpXmlObject

from eos_downloader.cli.utils import AliasedTyperGroup

from .command_helpers import (
    build_download_object,
    ensure_selection_mode_exclusive,
    maybe_run_interactive,
    print_exception_and_exit,
    progress_mode_from_flag,
    resolve_cvp_version,
)
from .utils import (
    initialize,
    search_version,
    download_files,
    handle_docker_import,
    download_from_containerlab_topology,
)

app = typer.Typer(
    cls=AliasedTyperGroup,
    no_args_is_help=True,
    help="Download Arista from Arista website",
)

_DEFAULT_OUTPUT = str(os.path.relpath(os.getcwd(), start=os.curdir))


@app.command()
def eos(
    ctx: typer.Context,
    format: str = typer.Option("vmdk", "--format", help="Image format"),
    output: str = typer.Option(
        _DEFAULT_OUTPUT, "--output", help="Path to save image", show_envvar=True
    ),
    latest: bool = typer.Option(
        False,
        "--latest",
        help="Get latest version. If --branch is not use, get the latest branch with specific release type",
        show_envvar=True,
    ),
    eve_ng: bool = typer.Option(
        False,
        "--eve-ng",
        help="Run EVE-NG vEOS provisioning (only if CLI runs on an EVE-NG server)",
        show_envvar=True,
    ),
    import_docker: bool = typer.Option(
        False,
        "--import-docker",
        help="Import docker image to local docker",
        show_envvar=True,
    ),
    skip_download: bool = typer.Option(
        False,
        "--skip-download",
        help="Skip download process - for debug only",
    ),
    docker_name: str = typer.Option(
        "arista/ceos",
        "--docker-name",
        help="Docker image name",
        show_envvar=True,
    ),
    docker_tag: Optional[str] = typer.Option(
        None,
        "--docker-tag",
        help="Docker image tag",
        show_envvar=True,
    ),
    version: Optional[str] = typer.Option(
        None,
        "--version",
        help="EOS version to download",
        show_envvar=True,
    ),
    release_type: str = typer.Option(
        RTYPE_FEATURE,
        "--release-type",
        help="Release type (M for Maintenance, F for Feature)",
        show_envvar=True,
    ),
    branch: Optional[str] = typer.Option(
        None,
        "--branch",
        help="Branch to download",
        show_envvar=True,
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        help="Enable dry-run mode: only run code without system changes",
    ),
    force: bool = typer.Option(
        False,
        "--force",
        help="Force download/import even if cached files or Docker images exist",
        show_envvar=True,
    ),
    no_progress: bool = typer.Option(
        False,
        "--no-progress",
        help="Disable the download progress display (useful for CI/non-TTY)",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        "-i",
        help="Open a guided wizard to pick format, version and options",
    ),
    containerlab_topology: Optional[Path] = typer.Option(
        None,
        "--containerlab-topology",
        "--clab",
        help="Path to containerlab topology file to download all cEOS images.",
        exists=True,
        dir_okay=False,
        resolve_path=True,
    ),
) -> int:
    """Download EOS image from Arista server."""
    # pylint: disable=unused-variable
    console, token, debug, log_level = initialize(ctx)
    progress: ProgressMode = progress_mode_from_flag(no_progress)

    if interactive:
        result = maybe_run_interactive(
            "eos",
            console,
            token,
            output,
            interactive=interactive,
            version=version,
            latest=latest,
            branch=branch,
        )
        if result is None:
            raise typer.Exit()
        format = result.image_format
        version = result.version
        output = result.output
        force = result.force
        import_docker = result.import_docker
        docker_name = result.docker_name
        docker_tag = result.docker_tag
        eve_ng = result.eve_ng
    elif containerlab_topology is not None:
        ensure_selection_mode_exclusive(
            version,
            latest,
            branch,
            option_name="--containerlab-topology",
        )
        # Auto-default to cEOS format when using containerlab topology
        ceos_formats = ("cEOS", "cEOS64", "cEOSarm")
        if format not in ceos_formats:
            console.print(
                f"[yellow]Format '{format}' is not a cEOS format. "
                f"Auto-defaulting to 'cEOS' for containerlab topology.[/yellow]"
            )
            format = "cEOS"
        return download_from_containerlab_topology(
            console=console,
            token=token,
            topology_file=Path(containerlab_topology),
            image_format=format,
            output=output,
            docker_name=docker_name,
            docker_tag=docker_tag,
            dry_run=dry_run,
            force=force,
            debug=debug,
            skip_download=skip_download,
            progress=progress,
        )

    if not interactive:
        version = search_version(
            console, token, version, latest, branch, format, release_type
        )
    if version is None:
        raise ValueError("Version is not set correctly")
    eos_dl_obj = build_download_object(
        EosXmlObject,
        console,
        debug,
        searched_version=version,
        token=token,
        image_type=format,
    )

    cli = SoftManager(dry_run=dry_run, force_download=force, console=console)

    if not skip_download:
        if not eve_ng:
            download_files(
                console, cli, eos_dl_obj, output, debug=debug, progress=progress
            )
        else:
            try:
                cli.provision_eve(eos_dl_obj, noztp=True, progress=progress)
            except Exception as error:  # pylint: disable=broad-exception-caught
                print_exception_and_exit(console, debug, error)

    if import_docker:
        if dry_run:
            effective_tag = docker_tag or version
            console.print(
                f"[DRY-RUN] Would import docker image [green]{docker_name}:{effective_tag}[/green]"
            )
            return 0
        return handle_docker_import(
            console, cli, eos_dl_obj, output, docker_name, docker_tag, debug, force
        )

    return 0


@app.command()
def cvp(
    ctx: typer.Context,
    latest: bool = typer.Option(
        False,
        "--latest",
        help="Get latest version. If --branch is not use, get the latest branch with specific release type",
        show_envvar=True,
    ),
    format: str = typer.Option(
        "ova",
        "--format",
        help="Image format",
        show_envvar=True,
    ),
    output: str = typer.Option(
        _DEFAULT_OUTPUT,
        "--output",
        help="Path to save image",
        show_envvar=True,
    ),
    version: Optional[str] = typer.Option(
        None,
        "--version",
        help="EOS version to download",
        show_envvar=True,
    ),
    branch: Optional[str] = typer.Option(
        None,
        "--branch",
        help="Branch to download",
        show_envvar=True,
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        help="Enable dry-run mode: only run code without system changes",
    ),
    force: bool = typer.Option(
        False,
        "--force",
        help="Force download even if cached files exist",
        show_envvar=True,
    ),
    no_progress: bool = typer.Option(
        False,
        "--no-progress",
        help="Disable the download progress display (useful for CI/non-TTY)",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        "-i",
        help="Open a guided wizard to pick format, version and options",
    ),
) -> int:
    """Download CVP image from Arista server."""
    # pylint: disable=unused-variable
    console, token, debug, log_level = initialize(ctx)
    progress: ProgressMode = progress_mode_from_flag(no_progress)

    if interactive:
        result = maybe_run_interactive(
            "cvp",
            console,
            token,
            output,
            interactive=interactive,
            version=version,
            latest=latest,
            branch=branch,
        )
        if result is None:
            raise typer.Exit()
        format = result.image_format
        version = result.version
        output = result.output
        force = result.force

    version = resolve_cvp_version(
        console,
        token,
        version=version,
        latest=latest,
        branch=branch,
        file_format=format,
        debug=debug,
    )

    console.print(f"version to download is {version}")

    if version is None:
        raise ValueError("Version is not set correctly")
    cvp_dl_obj = build_download_object(
        CvpXmlObject,
        console,
        debug,
        searched_version=version,
        token=token,
        image_type=format,
    )

    cli = SoftManager(dry_run=dry_run, force_download=force, console=console)
    download_files(
        console,
        cli,
        cvp_dl_obj,
        output,
        debug=debug,
        progress=progress,
        checksum_format="md5sum",
    )

    console.print(f"CVP file is saved under: {output}")
    return 0


@app.command()
def path(
    ctx: typer.Context,
    source: Optional[str] = typer.Option(
        None,
        "--source",
        "-s",
        help="Image path to download from Arista Website",
    ),
    output: str = typer.Option(
        _DEFAULT_OUTPUT,
        "--output",
        "-o",
        help="Path to save downloaded package",
        show_envvar=True,
    ),
    import_docker: bool = typer.Option(
        False,
        "--import-docker",
        help="Import docker image to local docker",
        show_envvar=True,
    ),
    docker_name: str = typer.Option(
        "arista/ceos:raw",
        "--docker-name",
        help="Docker image name",
        show_envvar=True,
    ),
    docker_tag: str = typer.Option(
        "dev",
        "--docker-tag",
        help="Docker image tag",
        show_envvar=True,
    ),
    force: bool = typer.Option(
        False,
        "--force",
        help="Force download/import even if cached files or Docker images exist",
        show_envvar=True,
    ),
    no_progress: bool = typer.Option(
        False,
        "--no-progress",
        help="Disable the download progress display (useful for CI/non-TTY)",
    ),
) -> int:
    """Download image from Arista server using direct path."""
    console, token, debug, log_level = initialize(ctx)
    progress: ProgressMode = progress_mode_from_flag(no_progress)

    if source is None:
        console.print("[red]Source is not set correctly ![/red]")
        raise typer.Exit(1)

    filename = os.path.basename(source)

    console.print(f"Downloading file {filename} from source: {source}")
    console.print(f"Saving file to: {output}")

    ar_server = AristaServer(token=token)

    try:
        file_url = ar_server.get_url(source)
        if log_level == "debug":
            console.print(f"URL to download file is: {file_url}")
    except Exception as error:  # pylint: disable=broad-exception-caught
        print_exception_and_exit(console, debug, error)

    if file_url is None:
        console.print("File URL is set to None when we expect a string")
        raise typer.Exit(1)

    # At this point, mypy knows file_url is not None due to the check above
    assert file_url is not None  # Type assertion for mypy

    cli = SoftManager(dry_run=False, force_download=force, console=console)

    try:
        cli.download_file(
            file_url, output, filename=filename, force=force, progress=progress
        )
    except Exception as error:  # pylint: disable=broad-exception-caught
        print_exception_and_exit(console, debug, error)

    if import_docker:
        console.print(
            f"Importing docker image [green]{docker_name}:{docker_tag}[/green] "
            f"from [blue]{os.path.join(output, filename)}[/blue]..."
        )

        try:
            cli.import_docker(
                local_file_path=os.path.join(output, filename),
                docker_name=docker_name,
                docker_tag=docker_tag,
                force=force,
            )
        except FileNotFoundError as exc:
            if debug:
                console.print_exception(show_locals=True)
            else:
                console.print(
                    f"\n[red]File not found: {os.path.join(output, filename)}[/red]"
                )
            raise typer.Exit(1) from exc

        console.print(
            f"Docker image imported successfully: [green]{docker_name}:{docker_tag}[/green]"
        )

    return 0
