"""Template query specifications — Phase 11.4."""
from __future__ import annotations

from dataclasses import dataclass
from uuid import UUID

from sqlalchemy import Select, asc, desc, or_
from sqlalchemy.sql import ColumnElement

from app.models.templates import (
    ExperienceTemplate,
    ExperienceTemplateVersion,
    TemplateInstantiation,
)
from app.models.types import (
    TemplateInstantiationStatus,
    TemplateStatus,
    TemplateVersionStatus,
)

TEMPLATE_SORT_FIELDS = frozenset(
    {
        "created_at",
        "updated_at",
        "code",
        "name",
        "status",
        "page_type",
        "category",
    }
)
TEMPLATE_VERSION_SORT_FIELDS = frozenset(
    {"created_at", "updated_at", "version_number", "status", "published_at"}
)
INSTANTIATION_SORT_FIELDS = frozenset(
    {"created_at", "updated_at", "completed_at", "status"}
)


@dataclass(frozen=True, slots=True)
class TemplateListSpec:
    workspace_id: UUID | None = None
    status: TemplateStatus | str | None = None
    page_type: str | None = None
    is_starter_pack: bool | None = None
    category: str | None = None
    q: str | None = None
    sort_by: str = "created_at"
    sort_dir: str = "desc"

    def filter_clauses(self) -> list[ColumnElement]:
        clauses: list[ColumnElement] = []
        if self.workspace_id is not None:
            clauses.append(ExperienceTemplate.workspace_id == self.workspace_id)
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, TemplateStatus)
                else TemplateStatus(str(self.status))
            )
            clauses.append(ExperienceTemplate.status == status)
        if self.page_type:
            clauses.append(ExperienceTemplate.page_type == self.page_type)
        if self.is_starter_pack is not None:
            clauses.append(ExperienceTemplate.is_starter_pack.is_(self.is_starter_pack))
        if self.category:
            clauses.append(ExperienceTemplate.category == self.category)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(
                or_(
                    ExperienceTemplate.code.ilike(term),
                    ExperienceTemplate.name.ilike(term),
                    ExperienceTemplate.description.ilike(term),
                    ExperienceTemplate.category.ilike(term),
                )
            )
        return clauses

    def apply(self, stmt: Select) -> Select:
        clauses = self.filter_clauses()
        if clauses:
            stmt = stmt.where(*clauses)
        sort_key = (
            self.sort_by if self.sort_by in TEMPLATE_SORT_FIELDS else "created_at"
        )
        column = getattr(ExperienceTemplate, sort_key)
        order = desc(column) if self.sort_dir.lower() != "asc" else asc(column)
        return stmt.order_by(order)


@dataclass(frozen=True, slots=True)
class TemplateVersionListSpec:
    template_id: UUID | None = None
    status: TemplateVersionStatus | str | None = None
    q: str | None = None
    sort_by: str = "version_number"
    sort_dir: str = "desc"

    def filter_clauses(self) -> list[ColumnElement]:
        clauses: list[ColumnElement] = []
        if self.template_id is not None:
            clauses.append(ExperienceTemplateVersion.template_id == self.template_id)
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, TemplateVersionStatus)
                else TemplateVersionStatus(str(self.status))
            )
            clauses.append(ExperienceTemplateVersion.status == status)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(ExperienceTemplateVersion.changelog.ilike(term))
        return clauses

    def apply(self, stmt: Select) -> Select:
        clauses = self.filter_clauses()
        if clauses:
            stmt = stmt.where(*clauses)
        sort_key = (
            self.sort_by
            if self.sort_by in TEMPLATE_VERSION_SORT_FIELDS
            else "version_number"
        )
        column = getattr(ExperienceTemplateVersion, sort_key)
        order = desc(column) if self.sort_dir.lower() != "asc" else asc(column)
        return stmt.order_by(order)


@dataclass(frozen=True, slots=True)
class InstantiationListSpec:
    template_id: UUID | None = None
    site_id: UUID | None = None
    page_id: UUID | None = None
    status: TemplateInstantiationStatus | str | None = None
    workspace_id: UUID | None = None
    sort_by: str = "created_at"
    sort_dir: str = "desc"

    def filter_clauses(self) -> list[ColumnElement]:
        clauses: list[ColumnElement] = []
        if self.template_id is not None:
            clauses.append(TemplateInstantiation.template_id == self.template_id)
        if self.site_id is not None:
            clauses.append(TemplateInstantiation.site_id == self.site_id)
        if self.page_id is not None:
            clauses.append(TemplateInstantiation.page_id == self.page_id)
        if self.workspace_id is not None:
            clauses.append(TemplateInstantiation.workspace_id == self.workspace_id)
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, TemplateInstantiationStatus)
                else TemplateInstantiationStatus(str(self.status))
            )
            clauses.append(TemplateInstantiation.status == status)
        return clauses

    def apply(self, stmt: Select) -> Select:
        clauses = self.filter_clauses()
        if clauses:
            stmt = stmt.where(*clauses)
        sort_key = (
            self.sort_by if self.sort_by in INSTANTIATION_SORT_FIELDS else "created_at"
        )
        column = getattr(TemplateInstantiation, sort_key)
        order = desc(column) if self.sort_dir.lower() != "asc" else asc(column)
        return stmt.order_by(order)
