"""Template repositories — Phase 11.4."""
from __future__ import annotations

from typing import Sequence
from uuid import UUID

from sqlalchemy import func, select

from app.models.templates import (
    ExperienceTemplate,
    ExperienceTemplateVersion,
    TemplateInstantiation,
)
from app.repositories.base import TenantBaseRepository
from app.specifications.templates import (
    InstantiationListSpec,
    TemplateListSpec,
    TemplateVersionListSpec,
)


class ExperienceTemplateRepository(TenantBaseRepository[ExperienceTemplate]):
    model = ExperienceTemplate

    async def get_by_code(
        self, tenant_id: UUID, workspace_id: UUID, code: str
    ) -> ExperienceTemplate | 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: TemplateListSpec | None = None,
    ) -> tuple[Sequence[ExperienceTemplate], 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 ExperienceTemplateVersionRepository(
    TenantBaseRepository[ExperienceTemplateVersion]
):
    model = ExperienceTemplateVersion

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

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: TemplateVersionListSpec | None = None,
    ) -> tuple[Sequence[ExperienceTemplateVersion], 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 TemplateInstantiationRepository(TenantBaseRepository[TemplateInstantiation]):
    model = TemplateInstantiation

    async def list_filtered(
        self,
        tenant_id: UUID,
        *,
        offset: int,
        limit: int,
        spec: InstantiationListSpec | None = None,
    ) -> tuple[Sequence[TemplateInstantiation], 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
