import argparse
import json
import logging
from contextlib import ExitStack
from pathlib import Path
from typing import get_args

from clan_lib.errors import ClanError
from clan_lib.machines.machines import Machine
from clan_lib.network.network import get_best_remote
from clan_lib.network.qr_code import read_qr_image, read_qr_json
from clan_lib.ssh.remote import HostKeyCheck, Remote

from clan_cli.completions import (
    add_dynamic_completer,
    complete_machines,
)

log = logging.getLogger(__name__)


def get_tor_remote(remotes: list[Remote]) -> Remote:
    """Get the Remote configured for SOCKS5 proxy (Tor)."""
    tor_remotes = [r for r in remotes if r.socks_port]

    if not tor_remotes:
        msg = "No socks5 proxy address provided, please provide a socks5 proxy address."
        raise ClanError(msg)

    if len(tor_remotes) > 1:
        msg = "Multiple socks5 proxy addresses provided, expected only one."
        raise ClanError(msg)

    return tor_remotes[0]


def ssh_command(args: argparse.Namespace) -> None:
    with ExitStack() as stack:
        remote: Remote
        if hasattr(args, "machine") and args.machine:
            machine = Machine(args.machine, args.flake)
            remote = stack.enter_context(get_best_remote(machine))
        elif args.png:
            data = read_qr_image(Path(args.png))
            qr_code = read_qr_json(data, args.flake)
            remote = stack.enter_context(qr_code.get_best_remote())
        elif args.json:
            json_file = Path(args.json)
            if json_file.is_file():
                data = json.loads(json_file.read_text())
            else:
                data = json.loads(args.json)

            qr_code = read_qr_json(data, args.flake)
            remote = stack.enter_context(qr_code.get_best_remote())
        else:
            msg = "No MACHINE, --json or --png data provided"
            raise ClanError(msg)

        # Convert ssh_option list to dictionary
        ssh_options = dict(args.ssh_option) if args.ssh_option else {}

        remote = remote.override(
            host_key_check=args.host_key_check,
            ssh_options=ssh_options,
        )
        if args.remote_command:
            remote.interactive_ssh(args.remote_command)
        else:
            remote.interactive_ssh()


def register_parser(parser: argparse.ArgumentParser) -> None:
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        "machine",
        type=str,
        nargs="?",
        metavar="MACHINE",
        help="Machine to ssh into (uses clan.core.networking.targetHost from configuration).",
    )

    group.add_argument(
        "-j",
        "--json",
        type=str,
        help=(
            "Deployment information as a JSON string or path to a JSON file "
            "(generated by starting the clan installer)."
        ),
    )
    group.add_argument(
        "-P",
        "--png",
        type=str,
        help="Deployment information as a QR code image file (generated by starting the clan installer).",
    )

    parser.add_argument(
        "--host-key-check",
        choices=list(get_args(HostKeyCheck)),
        default="tofu",
        help="Host key (.ssh/known_hosts) check mode.",
    )

    parser.add_argument(
        "--ssh-option",
        help="SSH option to set (can be specified multiple times)",
        nargs=2,
        metavar=("name", "value"),
        action="append",
        default=[],
    )

    parser.add_argument(
        "-c",
        "--remote-command",
        type=str,
        metavar="COMMAND",
        nargs=argparse.REMAINDER,
        help="Command to execute on the remote host, needs to be the LAST argument as it takes all remaining arguments.",
    )

    add_dynamic_completer(
        parser._actions[1],  # noqa: SLF001
        complete_machines,
    )  # assumes 'machine' is the first positional

    parser.set_defaults(func=ssh_command)
