"""Localization & media-ref query specifications — Phase 11.5."""
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.localization import (
    ExperienceLocalization,
    ExperienceMediaRef,
    SiteLocaleBinding,
)
from app.models.types import (
    LocalizationStatus,
    LocalizationTarget,
    MediaKind,
    MediaRefStatus,
    TextDirection,
)

SITE_LOCALE_BINDING_SORT_FIELDS = frozenset(
    {
        "created_at",
        "updated_at",
        "locale_code",
        "sort_order",
        "is_default",
        "text_direction",
    }
)
LOCALIZATION_SORT_FIELDS = frozenset(
    {
        "created_at",
        "updated_at",
        "locale",
        "status",
        "title",
        "text_direction",
        "target_type",
    }
)
MEDIA_REF_SORT_FIELDS = frozenset(
    {
        "created_at",
        "updated_at",
        "kind",
        "sort_order",
        "locale",
        "status",
        "storage_file_ref",
    }
)


@dataclass(frozen=True, slots=True)
class SiteLocaleBindingListSpec:
    workspace_id: UUID | None = None
    site_id: UUID | None = None
    locale_profile_id: UUID | None = None
    locale_code: str | None = None
    text_direction: TextDirection | str | None = None
    is_default: bool | None = None
    is_active: bool | 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.workspace_id is not None:
            clauses.append(SiteLocaleBinding.workspace_id == self.workspace_id)
        if self.site_id is not None:
            clauses.append(SiteLocaleBinding.site_id == self.site_id)
        if self.locale_profile_id is not None:
            clauses.append(
                SiteLocaleBinding.locale_profile_id == self.locale_profile_id
            )
        if self.locale_code:
            clauses.append(SiteLocaleBinding.locale_code == self.locale_code)
        if self.text_direction is not None:
            direction = (
                self.text_direction
                if isinstance(self.text_direction, TextDirection)
                else TextDirection(str(self.text_direction))
            )
            clauses.append(SiteLocaleBinding.text_direction == direction)
        if self.is_default is not None:
            clauses.append(SiteLocaleBinding.is_default.is_(self.is_default))
        if self.is_active is not None:
            clauses.append(SiteLocaleBinding.is_active.is_(self.is_active))
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(SiteLocaleBinding.locale_code.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 SITE_LOCALE_BINDING_SORT_FIELDS
            else "sort_order"
        )
        column = getattr(SiteLocaleBinding, 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 LocalizationListSpec:
    workspace_id: UUID | None = None
    target_type: LocalizationTarget | str | None = None
    target_id: UUID | None = None
    locale: str | None = None
    text_direction: TextDirection | str | None = None
    status: LocalizationStatus | 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(ExperienceLocalization.workspace_id == self.workspace_id)
        if self.target_type is not None:
            target = (
                self.target_type
                if isinstance(self.target_type, LocalizationTarget)
                else LocalizationTarget(str(self.target_type))
            )
            clauses.append(ExperienceLocalization.target_type == target)
        if self.target_id is not None:
            clauses.append(ExperienceLocalization.target_id == self.target_id)
        if self.locale:
            clauses.append(ExperienceLocalization.locale == self.locale)
        if self.text_direction is not None:
            direction = (
                self.text_direction
                if isinstance(self.text_direction, TextDirection)
                else TextDirection(str(self.text_direction))
            )
            clauses.append(ExperienceLocalization.text_direction == direction)
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, LocalizationStatus)
                else LocalizationStatus(str(self.status))
            )
            clauses.append(ExperienceLocalization.status == status)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(
                or_(
                    ExperienceLocalization.locale.ilike(term),
                    ExperienceLocalization.title.ilike(term),
                    ExperienceLocalization.summary.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 LOCALIZATION_SORT_FIELDS else "created_at"
        )
        column = getattr(ExperienceLocalization, 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 MediaRefListSpec:
    workspace_id: UUID | None = None
    site_id: UUID | None = None
    page_id: UUID | None = None
    owner_type: str | None = None
    owner_id: UUID | None = None
    kind: MediaKind | str | None = None
    locale: str | None = None
    status: MediaRefStatus | str | 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.workspace_id is not None:
            clauses.append(ExperienceMediaRef.workspace_id == self.workspace_id)
        if self.site_id is not None:
            clauses.append(ExperienceMediaRef.site_id == self.site_id)
        if self.page_id is not None:
            clauses.append(ExperienceMediaRef.page_id == self.page_id)
        if self.owner_type:
            clauses.append(ExperienceMediaRef.owner_type == self.owner_type)
        if self.owner_id is not None:
            clauses.append(ExperienceMediaRef.owner_id == self.owner_id)
        if self.kind is not None:
            kind = (
                self.kind
                if isinstance(self.kind, MediaKind)
                else MediaKind(str(self.kind))
            )
            clauses.append(ExperienceMediaRef.kind == kind)
        if self.locale:
            clauses.append(ExperienceMediaRef.locale == self.locale)
        if self.status is not None:
            status = (
                self.status
                if isinstance(self.status, MediaRefStatus)
                else MediaRefStatus(str(self.status))
            )
            clauses.append(ExperienceMediaRef.status == status)
        if self.q:
            term = f"%{self.q.strip()}%"
            clauses.append(
                or_(
                    ExperienceMediaRef.storage_file_ref.ilike(term),
                    ExperienceMediaRef.alt_text.ilike(term),
                    ExperienceMediaRef.caption.ilike(term),
                    ExperienceMediaRef.locale.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 MEDIA_REF_SORT_FIELDS else "sort_order"
        )
        column = getattr(ExperienceMediaRef, sort_key)
        order = desc(column) if self.sort_dir.lower() != "asc" else asc(column)
        return stmt.order_by(order)
