"""Custom slices-cli context."""

import os
from dataclasses import dataclass
from typing import Literal

import typer
from slices_clientlib_aiis.base.config import SlicesAIApiClientsConfig
from slices_clientlib_core.base.config import SlicesCentralApiClientsConfig, get_config_string
from slices_clientlib_core.oauth2.token import AuthJWT
from typer import Context as BaseContext

from . import err_console
from .api import ApiClients
from .utils import is_project_id


@dataclass
class ContextObj:
    """Context Object."""

    api: ApiClients
    user_info: AuthJWT | None
    verbosity: int
    project_id: str | None

    @property
    def central_config(self) -> SlicesCentralApiClientsConfig:
        """Return the Slices central config object."""
        return self.api.central_config

    @property
    def ai_config(self) -> SlicesAIApiClientsConfig:
        """Return the Slices BI config object."""
        return self.api.ai_config

    def get_logged_in_user(self) -> AuthJWT:
        """Get user info or show error."""
        if not self.user_info:
            err_console.print("[red]You must be logged in to perform this action. :key:")
            raise typer.Exit(128)
        return self.user_info

    def get_logged_in_userinfo(self, field_name: Literal["sub", "preferred_username"]):
        """Return logged in user info."""
        return self.get_logged_in_user()[field_name]

    def get_current_project_id(self) -> str | None:
        """Return the current project."""

        if project_id := os.environ.get("SLICES_PROJECT_ID"):
            if not is_project_id(project_id):
                err_console.print(
                    f"[red]Invalid project id '{project_id}' passed via environment variable 'SLICES_PROJECT_ID' :x:"
                )
            return os.environ["SLICES_PROJECT_ID"]

        project_id = get_config_string("default", section="project")

        if project_id and not is_project_id(project_id):
            err_console.print(f"[red]Invalid project id '{project_id}' :x:")
            err_console.print(
                "[red]Please check your configuration. "
                "You can set a valid project with [blue]slices project use <project name>[/]"
            )
            raise typer.Exit(130)
        return project_id

    def require_project_id(self) -> str:
        """Require a current project.

        Print an error and exit if not available.
        """
        project_id = self.get_current_project_id()
        if not project_id:
            err_console.print("[red]You must select a project to perform this action. :key:")
            err_console.print("Hint: Set a project with [blue]slices project use <project name>[/]")
            raise typer.Exit(129)

        return project_id

    def require_project_friendly_name(self) -> str:
        """Require a current project.

        Print an error and exit if not available.

        Return the friendly name of the project.
        """
        project_id = self.get_current_project_id()
        if not project_id:
            err_console.print("[red]You must select a project to perform this action. :key:")
            err_console.print("Hint: Set a project with [blue]slices project use <project name>[/]")
            raise typer.Exit(129)

        return self.api.project.get(project_id).name

    def get_current_project_token(self) -> str | None:
        """Return token for current project."""
        project_id = self.get_current_project_id()
        if not project_id:
            return None

        return self.api.get_project_jwt(project_id)


class Context(BaseContext):
    """Custom context for slices-cli."""

    obj: ContextObj
