"""Component query specifications — Phase 11.2."""
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.components import (
    ExperienceComponent,
    ExperienceComponentVersion,
    PageComponentPlacement,
)
from app.models.types import (
    ComponentKind,
    ComponentStatus,
    ComponentVersionStatus,
)

COMPONENT_SORT_FIELDS = frozenset(
    {"created_at", "updated_at", "code", "name", "kind", "status", "category"}
)
VERSION_SORT_FIELDS = frozenset(
    {"created_at", "updated_at", "version_number", "status", "published_at"}
)
PLACEMENT_SORT_FIELDS = frozenset(
    {"created_at", "updated_at", "sort_order", "slot_key"}
)


@dataclass(frozen=True, slots=True)
class ComponentListSpec:
    workspace_id: UUID | None = None
    kind: ComponentKind | str | None = None
    status: ComponentStatus | str | 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(ExperienceComponent.workspace_id == self.workspace_id)
        if self.kind is not None:
            kind = (
                self.kind
                if isinstance(self.kind, ComponentKind)
                else ComponentKind(str(self.kind))
            )
            clauses.append(ExperienceComponent.kind == kind)
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, ComponentStatus)
                else ComponentStatus(str(self.status))
            )
            clauses.append(ExperienceComponent.status == status)
        if self.category:
            clauses.append(ExperienceComponent.category == self.category)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(
                or_(
                    ExperienceComponent.code.ilike(term),
                    ExperienceComponent.name.ilike(term),
                    ExperienceComponent.description.ilike(term),
                    ExperienceComponent.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 COMPONENT_SORT_FIELDS else "created_at"
        )
        column = getattr(ExperienceComponent, 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 ComponentVersionListSpec:
    component_id: UUID | None = None
    status: ComponentVersionStatus | 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.component_id is not None:
            clauses.append(
                ExperienceComponentVersion.component_id == self.component_id
            )
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, ComponentVersionStatus)
                else ComponentVersionStatus(str(self.status))
            )
            clauses.append(ExperienceComponentVersion.status == status)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(ExperienceComponentVersion.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 VERSION_SORT_FIELDS else "version_number"
        )
        column = getattr(ExperienceComponentVersion, 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 PlacementListSpec:
    page_id: UUID | None = None
    site_id: UUID | None = None
    component_id: UUID | None = None
    q: str | None = None
    sort_by: str = "sort_order"
    sort_dir: str = "asc"

    def filter_clauses(self) -> list[ColumnElement]:
        clauses: list[ColumnElement] = []
        if self.page_id is not None:
            clauses.append(PageComponentPlacement.page_id == self.page_id)
        if self.site_id is not None:
            clauses.append(PageComponentPlacement.site_id == self.site_id)
        if self.component_id is not None:
            clauses.append(PageComponentPlacement.component_id == self.component_id)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(PageComponentPlacement.slot_key.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 PLACEMENT_SORT_FIELDS else "sort_order"
        )
        column = getattr(PageComponentPlacement, sort_key)
        order = desc(column) if self.sort_dir.lower() != "asc" else asc(column)
        return stmt.order_by(order)
