import datetime as dt
import uuid
from dataclasses import dataclass

from fastapi import HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import String, func, select
from sqlalchemy.orm import Session

from flyway.domain.clock import NZ

PAGE_SIZES = (10, 25, 50, 100)  # 文档 8.6


@dataclass
class Page:
    page: int
    page_size: int
    sort: str | None


class IdsIn(BaseModel):
    ids: list[uuid.UUID]


def page_params(page: int = Query(1, ge=1), page_size: int = 25, sort: str | None = None) -> Page:
    if page_size not in PAGE_SIZES:
        raise HTTPException(status_code=422, detail="page_size must be 10, 25, 50 or 100.")
    return Page(page, page_size, sort)


def paginate(db: Session, stmt, page: Page, sorts: dict, default: str) -> tuple[list, int]:
    """sort 形如 'name' 或 '-name'；只允许 sorts 中列出的键。"""
    key = page.sort or default
    column = sorts.get(key.lstrip("-"))
    if column is None:
        raise HTTPException(status_code=422, detail=f'Unknown sort "{key}".')
    if isinstance(column.type, String):
        column = func.lower(column)
    total = db.scalar(select(func.count()).select_from(stmt.order_by(None).subquery()))
    order = column.desc() if key.startswith("-") else column.asc()
    rows = db.scalars(stmt.order_by(order).offset((page.page - 1) * page.page_size).limit(page.page_size)).all()
    return list(rows), total


def nz_range(from_date: dt.date | None, to_date: dt.date | None) -> tuple[dt.datetime | None, dt.datetime | None]:
    """按新西兰日期筛选：from 当天 00:00 起，to 次日 00:00 止（不含）。"""
    start = dt.datetime.combine(from_date, dt.time(), NZ) if from_date else None
    end = dt.datetime.combine(to_date + dt.timedelta(days=1), dt.time(), NZ) if to_date else None
    return start, end


def like(column, keyword: str):
    return column.icontains(keyword.strip(), autoescape=True)
