from typing import Literal

import click
import rich

from tgzr.cli.add_plugins import find_group
from ..session import Session

from .workspaces import Workspaces, Workspace

from .dev_cli import add_dev_commands


def install_plugin(group: click.Group):
    group.add_command(ws)
    help = find_group(group, named="help")
    if help is None:
        raise Exception("`help` group not found, cannot install ws help commands!")
    help.add_command(ws_help)


@click.command("ws")
def ws_help():
    rich.print("Sorry, this help is not written yet :p")


@click.group(help="Manage tgzr workspaces")
@click.option(
    "-n", "--name", default=None, help="select an existing workspace by name."
)
@click.pass_context
def ws(ctx, name: str | None = None):
    session: Session = ctx.obj.session
    workspaces = Workspaces(parent=session.home)
    if name is not None:
        try:
            workspace = workspaces.get_workspace(name)
        except FileNotFoundError:
            raise click.UsageError(
                f'Workspace with name {name!r} does not exist in "{workspaces.home}".'
            )
    else:
        workspace = workspaces.get_default_workspace(ensure_exists=False)
    ctx.obj.workspaces = workspaces
    ctx.obj.workspace = workspace  # may be None


# register the dev command group:
add_dev_commands(ws)


@ws.command()
@click.argument("name")
@click.option("--allow-existing", is_flag=True, default=False)
@click.option(
    "--index", default=None, help="Override the default index (used for seed installs)."
)
@click.option(
    "-N",
    "--not-default",
    is_flag=True,
    default=False,
    help="Do not make this new workspace the default one.",
)
@click.pass_context
def create(
    ctx,
    name,
    allow_existing: bool = False,
    index: str | None = None,
    not_default: bool = True,
):
    """
    Create a new workspace with name "NAME".
    """
    session: Session = ctx.obj.session
    workspaces: Workspaces = ctx.obj.workspaces
    workspace = workspaces.get_workspace(name, ensure_exists=False)
    if workspace.exists():
        if not allow_existing:
            raise click.UsageError(
                f'A workspace {name!r} already exists in "{workspaces.home}". Use --allow-existing to update it.'
            )

    workspace.create(index=index)
    click.echo(f"Workspace {name} created.")

    # Ensure the main config is written so the session home can
    # be discovered:
    session.save_config()

    if not_default:
        return

    workspaces.set_default_workspace(name)
    ctx.obj.workspace = workspace
    click.echo(f"Workspace {name} set as default workspace.")


@ws.group(
    # invoke_without_command=True,
    help="Manage the workspaces configuration (Not the configuration for the current workspace!)",
)
@click.pass_context
def config(ctx):
    pass


@config.command("show")
@click.pass_context
def config_show(ctx):
    """
    Show the workspaces configuration.
    """
    workspaces: Workspaces = ctx.obj.workspaces
    rich.print(workspaces.config)


@config.command("set")
@click.pass_context
@click.option(
    "--default-index",
    help=(
        "Set the url of the default package index, like "
        '"https://pypi.org/simple", "/path/to/folder" or "./path/relative/to/workspace". '
        'Use "" to unset.'
    ),
)
def config_set(ctx, **kwargs):
    """
    Set config field(s).
    """
    workspaces: Workspaces = ctx.obj.workspaces

    not_None_options = [n for n, v in kwargs.items() if v is not None]
    if not not_None_options:
        raise click.UsageError("Please specify at least one option.")

    # print(kwargs)

    if (default_index := kwargs["default_index"]) is not None:
        if not default_index:
            default_index = None
        workspaces.config.default_index = default_index

    path = workspaces.save_config()
    click.echo(f"Config saved: {path}")


@ws.command()
@click.pass_context
def ls(ctx):
    """
    List the existing workspaces.
    """
    workspaces: Workspaces = ctx.obj.workspaces
    for ws in workspaces.get_workspaces():
        click.echo(f"  {ws.name} ({ws.path})")


@ws.command()
@click.pass_context
def show(ctx):
    """Show the current workspace config."""
    workspace: Workspace = ctx.obj.workspace
    if workspace is None:
        click.echo(
            'No workspace selected, use "tgzr ws --name NAME ..." to specify a workspace, or "tgzr ws select" to set the default one.'
        )
        return
    click.echo(f'Configuration for workspace "{workspace.name}" ({workspace.path}):')
    rich.print(workspace.config)


@ws.command()
@click.pass_context
def select(ctx):
    """
    Set the default workspace.
    """
    workspaces: Workspaces = ctx.obj.workspaces
    default_workspace_name = workspaces.default_workspace_name()
    workspace: Workspace = ctx.obj.workspace
    if workspace is not None and workspace.name == default_workspace_name:
        click.echo(f"The default workspace was already {default_workspace_name}.")
    else:
        workspaces.set_default_workspace(workspace.name)


@ws.command()
@click.pass_context
def switch(ctx):
    """
    Change the default workspace.
    """
    workspaces: Workspaces = ctx.obj.workspaces
    workspace_names = workspaces.workspace_names()
    if not workspace_names:
        click.echo('No workspace declared yet. Use "tgzr ws add" to add workspaces.')
        return

    click.echo("0 <None>")
    for i, name in enumerate(workspace_names, start=1):
        click.echo(f"{i} {name}")
    value = click.prompt("Enter workspace")
    if value.strip() == "0":
        workspaces.set_default_workspace(None)
        return

    name = None
    try:
        index = int(value)
    except ValueError:
        if value in workspace_names:
            name = value
    else:
        try:
            name = workspace_names[index - 1]
        except:
            pass

    if name is None:
        click.echo("Nope ¯\\_(ツ)_/¯")
    else:
        workspaces.set_default_workspace(name)


@ws.command()
@click.pass_context
@click.option(
    "--default-index",
    help=(
        "Set the url of the default package index, like "
        '"https://pypi.org/simple", "/path/to/folder" or "./path/relative/to/workspace". '
        'Use "" to unset.'
    ),
)
def set(ctx, **kwargs):
    """
    Set config field(s) on the current workspace.
    """
    workspace: Workspace = ctx.obj.workspace

    not_None_options = [n for n, v in kwargs.items() if v is not None]
    if not not_None_options:
        raise click.UsageError("Please specify at least one option.")

    # print(kwargs)

    if (default_index := kwargs["default_index"]) is not None:
        if not default_index:
            default_index = None
        workspace.config.default_index = default_index

    path = workspace.save_config()
    click.echo(f"Config saved: {path}")


@ws.command(
    context_settings=dict(
        ignore_unknown_options=True,
    )
)
@click.pass_context
@click.argument(
    "cmd_name",
)
@click.argument("cmd_args", nargs=-1, type=click.UNPROCESSED)
def run(ctx, cmd_name: str, cmd_args: list[str], tgzr_project: str | None):
    """
    Run a master cmd in the current workspace.
    """
    workspace: Workspace = ctx.obj.workspace
    workspace.run_cmd(cmd_name, cmd_args, project_name=None)


@ws.command
@click.option(
    "-s",
    "--shell",
    default="auto",
    help='The shell to use, one of ["xonsh", "cmd", "powershell", "auto"] (defaults to "auto").',
)
@click.pass_context
def shell(
    ctx,
    shell: Literal["xonsh", "cmd", "powershell", "auto"] = "auto",
):
    """
    Open a master shell in the current workspace.
    """
    workspace: Workspace = ctx.obj.workspace
    workspace.shell(shell_type=shell, project_name=None)


@ws.group(
    help="Manage the project in the current workspaces",
)
@click.argument("name")
@click.pass_context
def project(ctx, name):
    ctx.obj.project_name = name


@project.command(
    "run",
    context_settings=dict(
        ignore_unknown_options=True,
    ),
)
@click.pass_context
@click.argument(
    "cmd_name",
)
@click.argument("cmd_args", nargs=-1, type=click.UNPROCESSED)
def project_run(ctx, cmd_name: str, cmd_args: list[str]):
    """
    Run a cmd in a project of the current workspace.
    """
    workspace: Workspace = ctx.obj.workspace
    project_name = ctx.obj.project_name
    workspace.run_cmd(cmd_name, cmd_args, project_name)
