"""Slices CLI project."""

import os
from datetime import datetime
from enum import Enum
from typing import Annotated

import humanize
import typer
from msgspec._core import asdict
from msgspec.json import encode as json_encode
from msgspec.yaml import encode as yaml_encode
from pytz import UTC
from rich.table import Table
from slices_clientlib_core.base.config import set_config_string
from slices_clientlib_core.project import Project

from slices_cli_core import console, err_console
from slices_cli_core.core.context import Context
from slices_cli_core.core.users_info import EnrichedProjectMember
from slices_cli_core.util.time import friendly_datetime

project_app = typer.Typer(help="Projects", no_args_is_help=True)


class ListFormat(Enum):
    """List format options."""

    table = "table"
    json = "json"
    yaml = "yaml"


@project_app.command(
    "list-projects", help="Show projects of which you are a member", rich_help_panel="Commands", hidden=True
)
def list_projects_deprecated(
    ctx: Context,
    show_all: Annotated[bool, typer.Option(help="Also show expired projects")] = False,
    show_details: Annotated[bool, typer.Option(help="Show project IDs")] = False,
    format: Annotated[ListFormat, typer.Option("--format", "-f", help="Output format.")] = "table",  # type: ignore
):
    """List projects. Backwards compatible alias for `list`.

    Deprecation warning: `slices project list-project` is deprecated and will be removed later.
    Use `slices project list` instead.
    """
    err_console.print(
        "[orange3]:warning: `slices project list-project` is deprecated. Use `slices project list` instead."
    )
    return list_projects(ctx, show_all, show_details, format)


@project_app.command("list", help="Show projects of which you are a member", rich_help_panel="Commands")
def list_projects(
    ctx: Context,
    show_all: Annotated[bool, typer.Option(help="Also show expired projects")] = False,
    show_details: Annotated[bool, typer.Option(help="Show project IDs")] = False,
    format: Annotated[ListFormat, typer.Option("--format", "-f", help="Output format.")] = "table",  # type: ignore
):
    """List projects."""
    projects = ctx.obj.api.project.list(include_expired=show_all)

    if format == ListFormat.json:
        console.print_json(json_encode(projects).decode())
        return
    if format == ListFormat.yaml:
        console.print(yaml_encode(projects).decode())
        return

    columns = [
        "Name",
        "Role",
        "Created At",
        "Expires At",
    ]

    if show_details:
        columns.insert(0, "ID")

    table = Table(*columns, title=f"Projects for {ctx.obj.get_logged_in_userinfo('preferred_username')}")
    for project in projects:
        row = [
            project.name,
            project.role,
            friendly_datetime(project.created_at),
            friendly_datetime(project.expires_at) if project.expires_at else "-",
        ]
        if show_details:
            row.insert(0, project.id)

        table.add_row(*row)

    console.print(table)


class ShowProjectFormat(Enum):
    """Show project format Options."""

    text = "text"
    json = "json"
    yaml = "yaml"


class ProjectWithMembers(Project):
    """Project with members."""

    members: list[EnrichedProjectMember]


@project_app.command(help="Show the current project", rich_help_panel="Commands")
def show(
    ctx: Context,
    format: Annotated[ShowProjectFormat, typer.Option("--format", "-f", help="Output format.")] = "text",  # type: ignore
    show_members: Annotated[
        bool,
        typer.Option(
            "--show-members/--no-show-members",
            help="Show members. (Default: --show-members)",
            show_default=False,
        ),
    ] = True,
):
    """Show the current project, and it's members."""
    current_project_id = ctx.obj.get_current_project_id()

    if not current_project_id:
        err_console.print("[red]No project set. :x:")
        raise typer.Exit(1)

    project: Project | ProjectWithMembers = ctx.obj.api.project.get(current_project_id)

    if show_members:
        project_members = ctx.obj.other_users_info.get_project_members(current_project_id)
        project = ProjectWithMembers(**asdict(project), members=project_members)

    if format == ShowProjectFormat.json:
        console.print_json(json_encode(project).decode())
        return
    if format == ShowProjectFormat.yaml:
        console.print(yaml_encode(project).decode())
        return
    if format == ShowProjectFormat.text:
        console.print(f"[bold]The current project is '{project.name}'.")
        console.print(f":ticket: You are a {project.role} in this project.")

        if project.expires_at:
            humanized_delta = humanize.precisedelta(
                project.expires_at - datetime.now(UTC),
                minimum_unit="days",
                format="%0.0f",
            )
            if project.expires_at < datetime.now(UTC):
                console.print(
                    f"[red]:clock10: It expired on {friendly_datetime(project.expires_at)} ({humanized_delta} ago)."
                )
            else:
                console.print(f":clock10: It expires on {friendly_datetime(project.expires_at)} (in {humanized_delta})")
        else:
            console.print("[green]:clock10: It never expires.")

        if isinstance(project, ProjectWithMembers):
            table = Table("User ID", "Name", "Email", "Username", "Role", title=f"Members of project {project.name}")

            for member in project.members:
                try:
                    extra_user_info = ctx.obj.api.user.get(member.user_id)
                except Exception as e:  # noqa: E722
                    err_console.print(f"Warning: problem fetching extra user info: {e}")
                    extra_user_info = None
                name = f"{extra_user_info.first_name} {extra_user_info.last_name}" if extra_user_info else "unknown"
                email = extra_user_info.email if extra_user_info else "unknown"
                preferred_username = extra_user_info.preferred_username if extra_user_info else "unknown"
                table.add_row(member.user_id, name, email, preferred_username, member.role)
            console.print(table)


@project_app.command(help="Set the current project", rich_help_panel="Commands")
def use(
    ctx: Context,
    name: Annotated[str, typer.Argument(help="The ID or the name of the project that you want to set as default.")],
):
    """Set the current project."""

    if "SLICES_PROJECT_ID" in os.environ:
        err_console.print("[red]Project is set via the SLICES_PROJECT_ID environment variable. :x:")
        raise typer.Exit(1)

    projects = ctx.obj.api.project.list()

    project = None
    for candidate_project in projects:
        if name in (candidate_project.id, candidate_project.name):
            project = candidate_project
            break

    if not project:
        err_console.print(f"[red]Could not find project '{name}'. :x:")
        err_console.print(f"The following projects are available: {', '.join(project.name for project in projects)}")
        raise typer.Exit(1)

    set_config_string("default", project.id, "project")

    console.print(
        f"[green]The current project was set to '[bold]{project.name}[/]', "
        f"""in which you are a [bold]{project.role}[/] and which {
            f"expires on {friendly_datetime(project.expires_at)}" if project.expires_at else "never expires"
        }."""
    )


@project_app.command(help="Clear the current project", rich_help_panel="Advanced Commands")
def clear():
    """Clear the current project."""

    if "SLICES_PROJECT_ID" in os.environ:
        err_console.print("[red]Project is set via the SLICES_PROJECT_ID environment variable. :x:")
        raise typer.Exit(1)

    set_config_string("default", None, "project")
    console.print("Cleared current project.")


@project_app.command(help="Get a JWT project token", rich_help_panel="Advanced Commands")
def token(
    ctx: Context,
    name: Annotated[
        str | None, typer.Argument(help="The ID or the name of the project that you want to set as default.")
    ] = None,
):
    """Get a JWT project token."""

    if name:
        projects = ctx.obj.api.project.list()

        project = None
        for candidate_project in projects:
            if name in (candidate_project.id, candidate_project.name):
                project = candidate_project
                break

        if not project:
            err_console.print(f"[red]Could not find project '{name}'. :x:")
            err_console.print(
                f"The following projects are available: {', '.join(project.name for project in projects)}"
            )
            raise typer.Exit(1)

        current_project_id = project.id
    else:
        current_project_id = ctx.obj.get_current_project_id()

        if not current_project_id:
            err_console.print("[red]No project set. :x:")
            raise typer.Exit(1)

    project_token = ctx.obj.api.get_project_jwt(current_project_id)

    print(project_token)
