"""Active project helpers for ProjectService."""

from __future__ import annotations

from uuid import UUID

from noteflow.config.constants import ERROR_MSG_PROJECT_PREFIX, ERROR_MSG_WORKSPACE_PREFIX
from noteflow.domain.entities.project import Project

from ._types import ProjectActiveRepositoryProvider

ACTIVE_PROJECT_METADATA_KEY = "active_project_id"


class ActiveProjectMixin:
    """Set and resolve the active project for a workspace."""

    async def set_active_project(
        self,
        uow: ProjectActiveRepositoryProvider,
        workspace_id: UUID,
        project_id: UUID | None,
    ) -> None:
        """Set the active project for a workspace.

        Stores the active project ID in workspace metadata. Clearing the
        project_id removes the active override and falls back to default.

        Args:
            uow: Unit of work for persistence.
            workspace_id: Workspace UUID to update.
            project_id: Project UUID to set active, or None to clear.

        Raises:
            ValueError: If workspace or project does not exist, or project
                does not belong to the workspace.
        """
        if not uow.supports_workspaces:
            msg = "Workspaces not supported in this unit of work"
            raise NotImplementedError(msg)
        if not uow.supports_projects:
            msg = "Projects not supported in this unit of work"
            raise NotImplementedError(msg)

        workspace = await uow.workspaces.get(workspace_id)
        if workspace is None:
            msg = f"{ERROR_MSG_WORKSPACE_PREFIX}{workspace_id} not found"
            raise ValueError(msg)

        if project_id is not None:
            project = await uow.projects.get(project_id)
            if project is None:
                msg = f"{ERROR_MSG_PROJECT_PREFIX}{project_id} not found"
                raise ValueError(msg)
            if project.workspace_id != workspace_id:
                msg = f"Project {project_id} does not belong to workspace {workspace_id}"
                raise ValueError(msg)
            if project.is_archived:
                msg = f"{ERROR_MSG_PROJECT_PREFIX}{project_id} is archived"
                raise ValueError(msg)
            workspace.metadata[ACTIVE_PROJECT_METADATA_KEY] = str(project_id)
        else:
            workspace.metadata.pop(ACTIVE_PROJECT_METADATA_KEY, None)

        await uow.workspaces.update(workspace)

    async def get_active_project(
        self,
        uow: ProjectActiveRepositoryProvider,
        workspace_id: UUID,
    ) -> tuple[UUID | None, Project | None]:
        """Get the active project for a workspace.

        Returns the explicitly set active project if present; otherwise
        falls back to the workspace default project.

        Args:
            uow: Unit of work for persistence.
            workspace_id: Workspace UUID to resolve.

        Returns:
            Tuple of (active_project_id or None, resolved Project or None).

        Raises:
            ValueError: If workspace does not exist.
        """
        if not uow.supports_workspaces:
            msg = "Workspaces not supported in this unit of work"
            raise NotImplementedError(msg)
        if not uow.supports_projects:
            msg = "Projects not supported in this unit of work"
            raise NotImplementedError(msg)

        workspace = await uow.workspaces.get(workspace_id)
        if workspace is None:
            msg = f"{ERROR_MSG_WORKSPACE_PREFIX}{workspace_id} not found"
            raise ValueError(msg)

        active_project_id: UUID | None = None
        active_project: Project | None = None

        if raw_id := workspace.metadata.get(ACTIVE_PROJECT_METADATA_KEY):
            try:
                active_project_id = UUID(str(raw_id))
            except ValueError:
                active_project_id = None

        if active_project_id is not None:
            candidate = await uow.projects.get(active_project_id)
            if candidate and candidate.workspace_id == workspace_id and not candidate.is_archived:
                active_project = candidate
            else:
                active_project_id = None

        if active_project is None:
            active_project = await uow.projects.get_default_for_workspace(workspace_id)

        return active_project_id, active_project
