from __future__ import annotations
from typing import Literal


import click
import rich
import rich.table

from tgzr.cli.utils import TGZRCliGroup

from ..session import Session
from ..workspace import Workspace
from ..studio import Studio

from .utils import pass_session


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


@click.group(cls=TGZRCliGroup, help="Manage tgzr studios")
def studio():
    pass


@studio.command()
@click.argument("name")
@click.option("--allow-existing", is_flag=True, default=False)
@click.option(
    "--default-index",
    help="The URL of the default package index (by default: <https://pypi.org/simple>).",
)
@click.option(
    "-f",
    "--find-links",
    help="path a folder containing packages to install. Usefull in no-internet situations.",
)
@click.option(
    "--allow-prerelease",
    is_flag=True,
    help="Allow installing tgzr using pre-release packages. Default is False.",
)
@click.option(
    "-N",
    "--not-default",
    is_flag=True,
    default=False,
    help="Do not make this created studio the default one.",
)
@pass_session
def create(
    session: Session,
    name,
    allow_existing: bool = False,
    default_index: str | None = None,
    find_links: str | None = None,
    allow_prerelease: bool = False,
    not_default: bool = True,
):
    """
    Create a new Studio with name "NAME".
    """
    workspace: Workspace | None = session.workspace
    if workspace is None:
        raise click.UsageError(f"No workspace found at {session.home}.")
    studio = workspace.get_studio(name, ensure_exists=False)
    if studio.exists():
        if not allow_existing:
            raise click.UsageError(
                f'A Studio "{name}" already exists in workspace "{workspace.path}". Use --allow-existing to update it.'
            )

    studio.create(
        index=default_index,
        find_links=find_links,
        allow_prerelease=allow_prerelease,
    )
    click.echo(f"Studio {name!r} created.")

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

    if not_default:
        return

    workspace.set_default_studio(name)
    click.echo(f'Studio "{name}" set as default studio.')


@studio.command()
@pass_session
def ls(session: Session):
    """
    List the installed Studio.
    """
    workspace: Workspace | None = session.workspace
    if workspace is None:
        raise click.UsageError(f"No workspace found at {session.home}.")
    default_studio_name = workspace.default_studio_name()
    for studio in workspace.get_studios():
        default_indic = ""
        if studio.name == default_studio_name:
            default_indic = "*"
        click.echo(f"  {studio.name}{default_indic} ({studio.path})")


@studio.command()
@pass_session
def show(session: Session):
    """Show the studio config."""
    try:
        studio: Studio | None = session.get_selected_studio()
    except FileNotFoundError:
        raise click.UsageError(
            f'Studio "{session.selected_studio_name}" does not exists.'
        )
    if studio is None:
        raise click.UsageError(
            "No Studio selected, use `tgzr --studio <name> ...` to specify one, or `tgzr studio select` to set the default one."
        )

    click.echo(f'Configuration for Studio "{studio.name}" ({studio.path}):')
    rich.print(studio.config)


@studio.command()
@pass_session
def select(session: Session):
    """
    Set the default studio (without -S or --studio, resets default to None).
    """
    workspace: Workspace | None = session.workspace
    if workspace is None:
        raise click.UsageError(f"No workspace found at {session.home}.")

    default_studio_name = workspace.default_studio_name()
    studio: Studio | None = session.get_selected_studio()
    if studio is None:
        workspace.set_default_studio(None)
    else:
        if studio.name == default_studio_name:
            click.echo(f"The default Studio is already {default_studio_name}.")
        else:
            workspace.set_default_studio(studio.name)


@studio.command()
@pass_session
def switch(session: Session):
    """
    Change the default studio.
    """
    workspace: Workspace | None = session.workspace
    if workspace is None:
        raise click.UsageError(f"No workspace found at {session.home}.")

    studio_names = workspace.studio_names()
    if not studio_names:
        click.echo('No Studio installed yet. Use "tgzr studio create" to add one.')
        return

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

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

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


@studio.command()
@pass_session
@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(session: Session, **kwargs):
    """
    Set some config field in the Studio config.
    """
    studio: Studio | None = session.get_selected_studio()
    if studio is None:
        raise click.UsageError("Please select a studio or set a default one.")

    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
        studio.config.default_index = default_index

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


@studio.command(
    context_settings=dict(
        ignore_unknown_options=True,
    )
)
@click.argument(
    "cmd_name",
)
@click.argument("cmd_args", nargs=-1, type=click.UNPROCESSED)
@pass_session
def run(session: Session, cmd_name: str, cmd_args: list[str]):
    """
    Run a studio cmd.
    """
    studio: Studio | None = session.get_selected_studio()
    if studio is None:
        raise click.UsageError("Please select a studio or set a default one.")

    studio.run_cmd(cmd_name, cmd_args, project_name=None)


@studio.command
@click.option(
    "-s",
    "--shell",
    default="auto",
    help='The shell to use, one of ["xonsh", "bash", "cmd", "powershell", "auto"] (defaults to "auto").',
)
@pass_session
def shell(
    session: Session,
    shell: Literal["xonsh", "cmd", "powershell", "auto"] = "auto",
):
    """
    Open a shell configured for the studio.
    """
    studio: Studio | None = session.get_selected_studio()
    if studio is None:
        raise click.UsageError("Please select a studio or set a default one.")
    studio.shell(shell_type=shell, project_name=None)


@studio.command()
@click.option(
    "-f",
    "--group-filter",
    help="Show only this type of plugins (defaults is to show all).",
)
@pass_session
def plugins(session: Session, group_filter: str | None):
    """Show the plugins installed in the studio venv."""
    try:
        studio: Studio | None = session.get_selected_studio()
    except FileNotFoundError:
        raise click.UsageError(
            f'Studio "{session.selected_studio_name}" does not exists.'
        )
    if studio is None:
        raise click.UsageError(
            "No Studio selected, use `tgzr --studio <name> ...` to specify one, or `tgzr studio select` to set the default one."
        )

    plugins = studio.get_plugins(None, group_filter)
    table = rich.table.Table(
        "Group",
        "Name",
        "Value",
        title=f'Plugins in Studio "{studio.name}" ({studio.path})',
        min_width=80,
    )
    for plugin, distribution in plugins:
        package = f"{distribution.name} {distribution.version}"
        editable = distribution.origin and distribution.origin.dir_info.editable
        if editable:
            package = f"[orange1]{package}[/orange1]"
        table.add_row(
            plugin.group,
            plugin.name,
            plugin.value,
            package,
        )
    rich.print(table)
