"""Project CRUD operations for ProjectService."""

from __future__ import annotations

from typing import TYPE_CHECKING
from uuid import UUID, uuid4

from noteflow.domain.entities.project import Project, ProjectSettings, slugify
from noteflow.infrastructure.logging import get_logger

from ._types import ProjectCrudRepositoryProvider

if TYPE_CHECKING:
    from collections.abc import Sequence

logger = get_logger(__name__)


class ProjectCrudMixin:
    """CRUD operations for projects."""

    async def create_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        workspace_id: UUID,
        name: str,
        slug: str | None = None,
        description: str | None = None,
        settings: ProjectSettings | None = None,
    ) -> Project:
        """Create a new project in a workspace.

        Args:
            uow: Unit of work for database access.
            workspace_id: Parent workspace UUID.
            name: Project name.
            slug: Optional URL slug (auto-generated from name if not provided).
            description: Optional project description.
            settings: Optional project settings.

        Returns:
            Created project.

        Raises:
            NotImplementedError: If projects not supported.
        """
        if not uow.supports_projects:
            msg = "Projects require database persistence"
            raise NotImplementedError(msg)

        project_id = uuid4()
        generated_slug = slug or slugify(name)

        project = await uow.projects.create(
            project_id=project_id,
            workspace_id=workspace_id,
            name=name,
            slug=generated_slug,
            description=description,
            settings=settings,
        )
        await uow.commit()

        logger.info("Created project %s in workspace %s: %s", name, workspace_id, project_id)
        return project

    async def get_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        project_id: UUID,
    ) -> Project | None:
        """Get a project by ID.

        Args:
            uow: Unit of work for database access.
            project_id: Project UUID.

        Returns:
            Project if found, None otherwise.
        """
        return await uow.projects.get(project_id) if uow.supports_projects else None

    async def get_project_by_slug(
        self,
        uow: ProjectCrudRepositoryProvider,
        workspace_id: UUID,
        slug: str,
    ) -> Project | None:
        """Get a project by workspace and slug.

        Args:
            uow: Unit of work for database access.
            workspace_id: Workspace UUID.
            slug: Project slug.

        Returns:
            Project if found, None otherwise.
        """
        if not uow.supports_projects:
            return None

        return await uow.projects.get_by_slug(workspace_id, slug)

    async def get_default_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        workspace_id: UUID,
    ) -> Project | None:
        """Get the default project for a workspace.

        Args:
            uow: Unit of work for database access.
            workspace_id: Workspace UUID.

        Returns:
            Default project if exists, None otherwise.
        """
        if not uow.supports_projects:
            return None

        return await uow.projects.get_default_for_workspace(workspace_id)

    async def list_projects(
        self,
        uow: ProjectCrudRepositoryProvider,
        workspace_id: UUID,
        include_archived: bool = False,
        limit: int = 50,
        offset: int = 0,
    ) -> Sequence[Project]:
        """List projects in a workspace.

        Args:
            uow: Unit of work for database access.
            workspace_id: Workspace UUID.
            include_archived: Whether to include archived projects.
            limit: Maximum projects to return.
            offset: Pagination offset.

        Returns:
            List of projects.
        """
        if not uow.supports_projects:
            return []

        return await uow.projects.list_for_workspace(
            workspace_id,
            include_archived=include_archived,
            limit=limit,
            offset=offset,
        )

    async def update_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        project_id: UUID,
        name: str | None = None,
        slug: str | None = None,
        description: str | None = None,
        settings: ProjectSettings | None = None,
    ) -> Project | None:
        """Update a project.

        Args:
            uow: Unit of work for database access.
            project_id: Project UUID.
            name: New name (optional).
            slug: New slug (optional).
            description: New description (optional).
            settings: New settings (optional).

        Returns:
            Updated project if found, None otherwise.
        """
        if not uow.supports_projects:
            return None

        project = await uow.projects.get(project_id)
        if not project:
            return None

        if name is not None:
            project.update_name(name)
        if slug is not None:
            project.update_slug(slug)
        if description is not None:
            project.update_description(description)
        if settings is not None:
            project.update_settings(settings)

        updated = await uow.projects.update(project)
        await uow.commit()

        logger.info("Updated project: %s", project_id)
        return updated

    async def archive_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        project_id: UUID,
    ) -> Project | None:
        """Archive a project.

        Args:
            uow: Unit of work for database access.
            project_id: Project UUID.

        Returns:
            Archived project if found, None otherwise.

        Raises:
            CannotArchiveDefaultProjectError: If project is the default.
        """
        if not uow.supports_projects:
            return None

        project = await uow.projects.archive(project_id)
        if project:
            await uow.commit()
            logger.info("Archived project: %s", project_id)

        return project

    async def restore_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        project_id: UUID,
    ) -> Project | None:
        """Restore an archived project.

        Args:
            uow: Unit of work for database access.
            project_id: Project UUID.

        Returns:
            Restored project if found, None otherwise.
        """
        if not uow.supports_projects:
            return None

        project = await uow.projects.restore(project_id)
        if project:
            await uow.commit()
            logger.info("Restored project: %s", project_id)

        return project

    async def delete_project(
        self,
        uow: ProjectCrudRepositoryProvider,
        project_id: UUID,
    ) -> bool:
        """Delete a project permanently.

        Args:
            uow: Unit of work for database access.
            project_id: Project UUID.

        Returns:
            True if deleted, False if not found.
        """
        if not uow.supports_projects:
            return False

        deleted = await uow.projects.delete(project_id)
        if deleted:
            await uow.commit()
            logger.info("Deleted project: %s", project_id)

        return deleted

    async def count_projects(
        self,
        uow: ProjectCrudRepositoryProvider,
        workspace_id: UUID,
        include_archived: bool = False,
    ) -> int:
        """Count projects in a workspace.

        Args:
            uow: Unit of work for database access.
            workspace_id: Workspace UUID.
            include_archived: Whether to include archived projects.

        Returns:
            Project count.
        """
        if not uow.supports_projects:
            return 0

        return await uow.projects.count_for_workspace(workspace_id, include_archived)
