import builtins
import logging
from collections.abc import Sequence
from typing import Optional, Union
from uuid import UUID

from radicli import Arg
from wasabi import msg

from prodigy_teams_broker_sdk import models as broker_models
from prodigy_teams_pam_sdk.models import (
    ProjectCreating,
    ProjectDetail,
    ProjectSummary,
    ProjectUpdating,
)

from ..auth import AuthState
from ..cli import cli
from ..errors import BrokerError, CLIError, HTTPXErrors, ProdigyTeamsErrors
from ..messages import Messages
from ..query import resolve_project, resolve_project_id
from ..ui import print_info_table, print_mutation_result, print_table_with_select
from ._state import get_auth_state

logger = logging.getLogger(__name__)


@cli.subcommand(
    "projects",
    "list",
    # fmt: off
    select=Arg(
        "--select",
        help=Messages.select.format(opts=builtins.list(ProjectSummary.model_fields)),
    ),
    name=Arg("--name", help=Messages.filter_by.format(filter="name")),
    as_json=Arg("--json", help=Messages.as_json),
    # fmt: on
)
def list(
    select: builtins.list[str] = ["id", "name"],
    name: Optional[str] = None,
    as_json: bool = False,
) -> Sequence[ProjectSummary]:
    """List all projects"""
    client = get_auth_state().client
    items = builtins.list(client.project.all(name=name))
    print_table_with_select(items, select=select, as_json=as_json)
    return items


@cli.subcommand(
    "projects",
    "info",
    name_or_id=Arg(help=Messages.name_or_id.format(noun="project")),
    select=Arg(
        "--select",
        help=Messages.select.format(opts=builtins.list(ProjectDetail.model_fields)),
    ),
    as_json=Arg("--json", help=Messages.as_json),
)
def info(
    name_or_id: Union[str, UUID],
    select: Optional[builtins.list[str]] = None,
    as_json: bool = False,
) -> ProjectDetail:
    """Get detailed info for a project"""
    res = resolve_project(name_or_id)
    print_info_table(res, as_json=as_json, select=select)
    return res


@cli.subcommand(
    "projects",
    "create",
    name=Arg(help=Messages.name.format(noun="project")),
    description=Arg(help=Messages.description.format(noun="project")),
    default_job_name_template=Arg(
        "--default-job-name-template",
        help="Default name template for new jobs (e.g. '{recipe}_{date}')",
    ),
    exists_ok=Arg("--exists-ok", help=Messages.exists_ok),
    as_json=Arg("--json", help=Messages.as_json),
)
def create(
    name: str,
    description: str,
    default_job_name_template: Optional[str] = None,
    exists_ok: bool = False,
    as_json: bool = False,
) -> UUID | None:
    """Create a new project"""
    auth = get_auth_state()
    client = auth.client
    body = ProjectCreating(
        name=name,
        description=description,
        default_job_name_template=default_job_name_template,
    )
    try:
        res = client.project.create(body)
    except ProdigyTeamsErrors.ProjectExists:
        if exists_ok:
            msg.info(Messages.T001.format(noun="project", name=name))
            return None
        raise CLIError(Messages.E002.format(noun="project", name=name))
    except ProdigyTeamsErrors.ProjectInvalid:
        raise CLIError(Messages.E004.format(noun="project", name=name))
    print_mutation_result(
        {"id": str(res.id), "name": res.name},
        Messages.T002.format(noun="project", name=res.name),
        as_json=as_json,
    )
    if not as_json:
        print_info_table(res)
    return res.id


@cli.subcommand(
    "projects",
    "update",
    name_or_id=Arg(help=Messages.name_or_id.format(noun="project")),
    name=Arg("--name", help="New name for the project"),
    description=Arg("--description", help="New description for the project"),
    default_job_name_template=Arg(
        "--default-job-name-template",
        help="Default name template for new jobs (e.g. '{recipe}_{date}')",
    ),
    as_json=Arg("--json", help=Messages.as_json),
)
def update(
    name_or_id: Union[str, UUID],
    name: Optional[str] = None,
    description: Optional[str] = None,
    default_job_name_template: Optional[str] = None,
    as_json: bool = False,
) -> ProjectDetail:
    """Update a project"""
    project_id = resolve_project_id(name_or_id)
    auth = get_auth_state()
    body = ProjectUpdating(
        id=project_id,
        name=name,
        description=description,
        default_job_name_template=default_job_name_template,
    )
    res = auth.client.project.update(body)
    print_mutation_result(
        {"id": str(res.id), "name": res.name},
        Messages.T051.format(noun="project", name=res.name),
        as_json=as_json,
    )
    if not as_json:
        print_info_table(res)
    return res


@cli.subcommand(
    "projects",
    "delete",
    name_or_id=Arg(help=Messages.name_or_id.format(noun="project")),
    as_json=Arg("--json", help=Messages.as_json),
)
def delete(name_or_id: Union[str, UUID], as_json: bool = False) -> UUID:
    """Delete a project"""
    project_id = resolve_project_id(name_or_id)
    auth = get_auth_state()
    _stop_project_jobs(auth, project_id)
    try:
        auth.client.project.delete(id=project_id)
    except (
        ProdigyTeamsErrors.ProjectForbiddenDelete,
        ProdigyTeamsErrors.ProjectNotFound,
    ):
        raise CLIError(Messages.E006.format(noun="project", name=name_or_id))
    else:
        print_mutation_result(
            {"id": str(project_id), "deleted": True},
            Messages.T003.format(noun="project", name=name_or_id),
            as_json=as_json,
        )
    return project_id


def _stop_project_jobs(auth: AuthState, project_id: UUID) -> None:
    """Stop all broker jobs for a project's tasks, actions, and agents."""
    job_ids: builtins.list[tuple[UUID, broker_models.JobType]] = []
    for task in auth.client.task.all(project_id=project_id, page_size=100):
        job_ids.append((task.id, broker_models.JobType.task))
    for action in auth.client.action.all(project_id=project_id, page_size=100):
        job_ids.append((action.id, broker_models.JobType.action))
    for agent in auth.client.agent.all(project_id=project_id, page_size=100):
        job_ids.append((agent.id, broker_models.JobType.agent))
    for job_id, job_type in job_ids:
        try:
            auth.broker_client.jobs.stop_job(
                broker_models.JobID(id=job_id, job_type=job_type)
            )
        except (BrokerError, *HTTPXErrors):
            logger.debug(
                "Could not stop %s job %s (may not be running)", job_type.value, job_id
            )
