import logging
from pathlib import Path
from typing import Optional

import click
import coloredlogs

from hilda import launch_lldb
from hilda._version import version
from hilda.launch_lldb import (
    create_hilda_client_using_attach_by_name,
    create_hilda_client_using_attach_by_pid,
    create_hilda_client_using_launch,
    create_hilda_client_using_remote_attach,
)

DEFAULT_HILDA_PORT = 1234

coloredlogs.install(level=logging.DEBUG)


@click.group()
def cli():
    pass


startup_files_option = click.option("-f", "--startup_files", multiple=True, help="Files to run on start")


def parse_envp(ctx: click.Context, param: click.Parameter, value: list[str]) -> list[str]:
    env_list = []
    for item in value:
        try:
            key, val = item.split("=", 1)
            env_list.append(f"{key}={val}")
        except ValueError as e:
            raise click.BadParameter(f"Invalid format for --envp: {item}. Expected KEY=VALUE.") from e
    return env_list


@cli.command("remote")
@click.argument("hostname", default="localhost")
@click.argument("port", type=click.INT, default=DEFAULT_HILDA_PORT)
@startup_files_option
def remote(hostname: str, port: int, startup_files: list[str]) -> None:
    """Connect to remote debugserver at given address"""
    with create_hilda_client_using_remote_attach(hostname, port) as hilda_client:
        hilda_client.interact(startup_files=startup_files)


@cli.command("attach")
@click.option("-n", "--name", help="process name to attach")
@click.option("-p", "--pid", type=click.INT, help="pid to attach")
@startup_files_option
def attach(name: Optional[str], pid: Optional[int], startup_files: list[str]) -> None:
    """Attach to given process and start a lldb shell"""
    if name is not None:
        hilda_client = create_hilda_client_using_attach_by_name(name)
    elif pid is not None:
        hilda_client = create_hilda_client_using_attach_by_pid(pid)
    else:
        raise click.UsageError("You must specify a process name or pid")
    hilda_client.interact(startup_files=startup_files)
    hilda_client.detach()


@cli.command("bare")
def cli_bare() -> None:
    """Just start a lldb shell"""
    commands = [f"command script import {Path(__file__).resolve().parent / 'lldb_entrypoint.py'}"]
    commands = "\n".join(commands)
    launch_lldb.execute(f'lldb --one-line "{commands}"')


@cli.command("launch")
@click.argument("exec_path")
@click.option("--argv", multiple=True, help="Command line arguments to pass to the process")
@click.option("--envp", multiple=True, callback=parse_envp, help="Environment variables in the form KEY=VALUE")
@click.option("--stdin", help="Redirect stdin from this file path")
@click.option("--stdout", help="Redirect stdout to this file path")
@click.option("--stderr", help="Redirect stderr to this file path")
@click.option("--cwd", help="Set the working directory for the process")
@click.option("--flags", type=click.INT, default=0, help="Launch flags (bitmask)")
@startup_files_option
def launch(
    exec_path: str,
    argv: list[str],
    envp: list[str],
    stdin: Optional[Path],
    stdout: Optional[Path],
    stderr: Optional[Path],
    cwd: Optional[Path],
    flags: Optional[int],
    startup_files: list[str],
) -> None:
    """Attach to a given process and start a lldb shell"""
    argv = list(argv)
    envp = list(envp)
    with create_hilda_client_using_launch(exec_path, argv, envp, stdin, stdout, stderr, cwd, flags) as hilda_client:
        hilda_client.interact(startup_files=startup_files)


@cli.command("version")
def cli_version() -> None:
    """Show the version information."""
    click.echo(version)
