"""Component library repositories — Phase 11.2."""
from __future__ import annotations

from typing import Sequence
from uuid import UUID

from sqlalchemy import func, select

from app.models.components import (
    ExperienceComponent,
    ExperienceComponentVersion,
    PageComponentPlacement,
)
from app.repositories.base import TenantBaseRepository
from app.specifications.components import (
    ComponentListSpec,
    ComponentVersionListSpec,
    PlacementListSpec,
)


class ExperienceComponentRepository(TenantBaseRepository[ExperienceComponent]):
    model = ExperienceComponent

    async def get_by_code(
        self, tenant_id: UUID, workspace_id: UUID, code: str
    ) -> ExperienceComponent | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.workspace_id == workspace_id,
            self.model.code == code,
            self.model.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: ComponentListSpec | None = None,
    ) -> tuple[Sequence[ExperienceComponent], int]:
        base = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.is_deleted.is_(False),
        )
        count_base = select(func.count()).select_from(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.is_deleted.is_(False),
        )
        if spec is not None:
            base = spec.apply(base)
            for clause in spec.filter_clauses():
                count_base = count_base.where(clause)
        else:
            base = base.order_by(self.model.created_at.desc())
        total = int((await self.session.execute(count_base)).scalar_one())
        result = await self.session.execute(base.offset(offset).limit(limit))
        return result.scalars().all(), total


class ExperienceComponentVersionRepository(
    TenantBaseRepository[ExperienceComponentVersion]
):
    model = ExperienceComponentVersion

    async def get_by_number(
        self, tenant_id: UUID, component_id: UUID, version_number: int
    ) -> ExperienceComponentVersion | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.component_id == component_id,
            self.model.version_number == version_number,
            self.model.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def max_version_number(self, tenant_id: UUID, component_id: UUID) -> int:
        stmt = select(func.max(self.model.version_number)).where(
            self.model.tenant_id == tenant_id,
            self.model.component_id == component_id,
            self.model.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        value = result.scalar_one()
        return int(value or 0)

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: ComponentVersionListSpec | None = None,
    ) -> tuple[Sequence[ExperienceComponentVersion], int]:
        base = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.is_deleted.is_(False),
        )
        count_base = select(func.count()).select_from(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.is_deleted.is_(False),
        )
        if spec is not None:
            base = spec.apply(base)
            for clause in spec.filter_clauses():
                count_base = count_base.where(clause)
        else:
            base = base.order_by(self.model.version_number.desc())
        total = int((await self.session.execute(count_base)).scalar_one())
        result = await self.session.execute(base.offset(offset).limit(limit))
        return result.scalars().all(), total


class PageComponentPlacementRepository(TenantBaseRepository[PageComponentPlacement]):
    model = PageComponentPlacement

    async def get_by_slot(
        self, tenant_id: UUID, page_id: UUID, slot_key: str
    ) -> PageComponentPlacement | None:
        stmt = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.page_id == page_id,
            self.model.slot_key == slot_key,
            self.model.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def count_by_component(self, tenant_id: UUID, component_id: UUID) -> int:
        stmt = select(func.count()).select_from(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.component_id == component_id,
            self.model.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return int(result.scalar_one())

    async def count_by_version(
        self, tenant_id: UUID, component_version_id: UUID
    ) -> int:
        stmt = select(func.count()).select_from(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.component_version_id == component_version_id,
            self.model.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return int(result.scalar_one())

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: PlacementListSpec | None = None,
    ) -> tuple[Sequence[PageComponentPlacement], int]:
        base = select(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.is_deleted.is_(False),
        )
        count_base = select(func.count()).select_from(self.model).where(
            self.model.tenant_id == tenant_id,
            self.model.is_deleted.is_(False),
        )
        if spec is not None:
            base = spec.apply(base)
            for clause in spec.filter_clauses():
                count_base = count_base.where(clause)
        else:
            base = base.order_by(self.model.sort_order.asc())
        total = int((await self.session.execute(count_base)).scalar_one())
        result = await self.session.execute(base.offset(offset).limit(limit))
        return result.scalars().all(), total
