#!/usr/bin/env python
# coding: utf-8 -*-
# pylint: disable=no-value-for-parameter
# 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, Union
import click
from eos_downloader.models.data import RTYPE_FEATURE
from eos_downloader.logics.download import SoftManager
from eos_downloader.logics.arista_server import AristaServer
from eos_downloader.logics.arista_xml_server import (
    EosXmlObject,
    AristaXmlQuerier,
    CvpXmlObject,
)
from eos_downloader.exceptions import AuthenticationError

from .utils import (
    initialize,
    search_version,
    download_files,
    handle_docker_import,
    download_from_containerlab_topology,
)


@click.command()
@click.option("--format", default="vmdk", help="Image format", show_default=True)
@click.option(
    "--output",
    default=str(os.path.relpath(os.getcwd(), start=os.curdir)),
    help="Path to save image",
    type=click.Path(),
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--latest",
    is_flag=True,
    help="Get latest version. If --branch is not use, get the latest branch with specific release type",
    default=False,
    show_envvar=True,
)
@click.option(
    "--eve-ng",
    is_flag=True,
    help="Run EVE-NG vEOS provisioning (only if CLI runs on an EVE-NG server)",
    default=False,
    show_envvar=True,
)
@click.option(
    "--import-docker",
    is_flag=True,
    help="Import docker image to local docker",
    default=False,
    show_envvar=True,
)
@click.option(
    "--skip-download",
    is_flag=True,
    help="Skip download process - for debug only",
    default=False,
)
@click.option(
    "--docker-name",
    default="arista/ceos",
    help="Docker image name",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--docker-tag",
    default=None,
    help="Docker image tag",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--version",
    default=None,
    help="EOS version to download",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--release-type",
    default=RTYPE_FEATURE,
    help="Release type (M for Maintenance, F for Feature)",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--branch",
    default=None,
    help="Branch to download",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--dry-run",
    is_flag=True,
    help="Enable dry-run mode: only run code without system changes",
    default=False,
)
@click.option(
    "--force",
    is_flag=True,
    help="Force download/import even if cached files or Docker images exist",
    default=False,
    show_envvar=True,
)
@click.option(
    "--containerlab-topology",
    "--clab",
    default=None,
    help="Path to containerlab topology file to download all cEOS images.",
    type=click.Path(exists=True, dir_okay=False, resolve_path=True),
)
@click.pass_context
def eos(
    ctx: click.Context,
    format: str,
    output: str,
    eve_ng: bool,
    import_docker: bool,
    skip_download: bool,
    docker_name: str,
    docker_tag: str,
    version: Union[str, None],
    release_type: str,
    latest: bool,
    branch: Union[str, None],
    dry_run: bool,
    force: bool,
    containerlab_topology: Optional[str],
) -> int:
    """Download EOS image from Arista server."""
    # pylint: disable=unused-variable
    console, token, debug, log_level = initialize(ctx)

    if containerlab_topology is not None:
        if version is not None or latest or branch is not None:
            raise click.UsageError(
                "--containerlab-topology is mutually exclusive with --version, --latest, and --branch"
            )
        # 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,
        )

    version = search_version(
        console, token, version, latest, branch, format, release_type
    )
    if version is None:
        raise ValueError("Version is not set correctly")
    try:
        eos_dl_obj = EosXmlObject(
            searched_version=version, token=token, image_type=format
        )
    except Exception:
        console.print_exception(show_locals=True)
        ctx.exit(1)

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

    if not skip_download:
        if not eve_ng:
            download_files(
                console, cli, eos_dl_obj, output, rich_interface=True, debug=debug
            )
        else:
            try:
                cli.provision_eve(eos_dl_obj, noztp=True)
            except Exception as e:
                if debug:
                    console.print_exception(show_locals=True)
                else:
                    console.print(f"\n[red]Exception raised: {e}[/red]")
                ctx.exit(1)

    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


@click.command()
@click.option(
    "--format",
    default="ova",
    help="Image format",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--output",
    default=str(os.path.relpath(os.getcwd(), start=os.curdir)),
    help="Path to save image",
    type=click.Path(),
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--latest",
    is_flag=True,
    help="Get latest version. If --branch is not use, get the latest branch with specific release type",
    default=False,
    show_envvar=True,
)
@click.option(
    "--version",
    default=None,
    help="EOS version to download",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--branch",
    default=None,
    help="Branch to download",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--dry-run",
    is_flag=True,
    help="Enable dry-run mode: only run code without system changes",
    default=False,
)
@click.option(
    "--force",
    is_flag=True,
    help="Force download even if cached files exist",
    default=False,
    show_envvar=True,
)
@click.pass_context
def cvp(
    ctx: click.Context,
    latest: bool,
    format: str,
    output: str,
    version: Union[str, None],
    branch: Union[str, None],
    dry_run: bool = False,
    force: bool = False,
) -> int:
    """Download CVP image from Arista server."""
    # pylint: disable=unused-variable
    console, token, debug, log_level = initialize(ctx)

    if version is not None:
        console.print(
            f"Searching for EOS version [green]{version}[/green] for [blue]{format}[/blue] format..."
        )
    elif latest:
        console.print(
            f"Searching for [blue]latest[/blue] EOS version for [blue]{format}[/blue] format..."
        )
    elif branch is not None:
        console.print(
            f"Searching for EOS [b]latest[/b] version for [blue]{branch}[/blue] branch for [blue]{format}[/blue] format..."
        )

    if branch is not None or latest:
        try:
            querier = AristaXmlQuerier(token=token)
            version_obj = querier.latest(package="cvp", branch=branch)
            version = str(version_obj)
        except AuthenticationError as auth_error:
            console.print(f"[red]Authentication Error:[/red] {str(auth_error)}")
            ctx.exit(1)
        except Exception as e:
            console.print(f"Token is set to: {token}")
            console.print_exception(show_locals=True)
            ctx.exit(1)

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

    if version is None:
        raise ValueError("Version is not set correctly")
    try:
        cvp_dl_obj = CvpXmlObject(
            searched_version=version, token=token, image_type=format
        )
    except Exception as e:
        if debug:
            console.print_exception(show_locals=True)
        else:
            console.print(f"\n[red]Exception raised: {e}[/red]")
        ctx.exit(1)

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

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


@click.command()
@click.option(
    "--source",
    "-s",
    help="Image path to download from Arista Website",
    type=str,
    show_default=False,
    show_envvar=False,
)
@click.option(
    "--output",
    "-o",
    default=str(os.path.relpath(os.getcwd(), start=os.curdir)),
    help="Path to save downloaded package",
    type=click.Path(),
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--import-docker",
    is_flag=True,
    help="Import docker image to local docker",
    default=False,
    show_envvar=True,
)
@click.option(
    "--docker-name",
    default="arista/ceos:raw",
    help="Docker image name",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--docker-tag",
    default="dev",
    help="Docker image tag",
    show_default=True,
    show_envvar=True,
)
@click.option(
    "--force",
    is_flag=True,
    help="Force download/import even if cached files or Docker images exist",
    default=False,
    show_envvar=True,
)
@click.pass_context
# pylint: disable=too-many-branches
def path(
    ctx: click.Context,
    output: str,
    source: str,
    import_docker: bool,
    docker_name: str,
    docker_tag: str,
    force: bool,
) -> int:
    """Download image from Arista server using direct path."""
    console, token, debug, log_level = initialize(ctx)

    if source is None:
        console.print("[red]Source is not set correctly ![/red]")
        ctx.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 e:
        if debug:
            console.print_exception(show_locals=True)
        else:
            console.print(f"\n[red]Exception raised: {e}[/red]")
        ctx.exit(1)

    if file_url is None:
        console.print("File URL is set to None when we expect a string")
        ctx.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)

    try:
        cli.download_file(file_url, output, filename=filename, force=force)
    except Exception as e:
        if debug:
            console.print_exception(show_locals=True)
        else:
            console.print(f"\n[red]Exception raised: {e}[/red]")
        ctx.exit(1)

    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:
            if debug:
                console.print_exception(show_locals=True)
            else:
                console.print(
                    f"\n[red]File not found: {os.path.join(output, filename)}[/red]"
                )
            ctx.exit(1)

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

    return 0
