"""Chainable query builder that translates to SQL."""

from __future__ import annotations

from dataclasses import dataclass, field as dataclass_field
from typing import Any

from zuzu.field import Condition, CompoundCondition


@dataclass
class Query:
    """A chainable query that lazily builds SQL.

    Usage:
        q = Query(table="clients")
        q = q.where(Condition("status", "=", "active"))
        q = q.sort("revenue", descending=True)
        q = q.first(10)
        sql, params = q.build_select()
    """

    table: str
    conditions: list[Condition | CompoundCondition] = dataclass_field(default_factory=list)
    keyword_conditions: dict[str, Any] = dataclass_field(default_factory=dict)
    sort_field: str | None = None
    sort_descending: bool = False
    limit_n: int | None = None
    offset_n: int | None = None

    def add_condition(self, cond: Condition | CompoundCondition) -> Query:
        """Return a new Query with an added condition."""
        return Query(
            table=self.table,
            conditions=[*self.conditions, cond],
            keyword_conditions=self.keyword_conditions,
            sort_field=self.sort_field,
            sort_descending=self.sort_descending,
            limit_n=self.limit_n,
            offset_n=self.offset_n,
        )

    def add_keywords(self, **kwargs: Any) -> Query:
        """Return a new Query with keyword equality conditions."""
        return Query(
            table=self.table,
            conditions=self.conditions,
            keyword_conditions={**self.keyword_conditions, **kwargs},
            sort_field=self.sort_field,
            sort_descending=self.sort_descending,
            limit_n=self.limit_n,
            offset_n=self.offset_n,
        )

    def with_sort(self, field: str, descending: bool = False) -> Query:
        return Query(
            table=self.table,
            conditions=self.conditions,
            keyword_conditions=self.keyword_conditions,
            sort_field=field,
            sort_descending=descending,
            limit_n=self.limit_n,
            offset_n=self.offset_n,
        )

    def with_limit(self, n: int) -> Query:
        return Query(
            table=self.table,
            conditions=self.conditions,
            keyword_conditions=self.keyword_conditions,
            sort_field=self.sort_field,
            sort_descending=self.sort_descending,
            limit_n=n,
            offset_n=self.offset_n,
        )

    # ── SQL generation ───────────────────────────────────────────────────

    def build_select(self, columns: str = "*") -> tuple[str, list[Any]]:
        """Build a SELECT statement with parameters."""
        parts = [f"SELECT {columns} FROM {self.table}"]
        params: list[Any] = []

        where_clauses, where_params = self._build_where()
        if where_clauses:
            parts.append(f"WHERE {where_clauses}")
            params.extend(where_params)

        if self.sort_field:
            direction = "DESC" if self.sort_descending else "ASC"
            parts.append(f"ORDER BY {self.sort_field} {direction}")

        if self.limit_n is not None:
            parts.append(f"LIMIT {self.limit_n}")

        if self.offset_n is not None:
            parts.append(f"OFFSET {self.offset_n}")

        return " ".join(parts), params

    def build_count(self) -> tuple[str, list[Any]]:
        """Build a COUNT(*) statement."""
        parts = [f"SELECT COUNT(*) FROM {self.table}"]
        params: list[Any] = []

        where_clauses, where_params = self._build_where()
        if where_clauses:
            parts.append(f"WHERE {where_clauses}")
            params.extend(where_params)

        return " ".join(parts), params

    def build_aggregate(self, func: str, column: str) -> tuple[str, list[Any]]:
        """Build an aggregate statement (SUM, AVG, etc.)."""
        parts = [f"SELECT {func}({column}) FROM {self.table}"]
        params: list[Any] = []

        where_clauses, where_params = self._build_where()
        if where_clauses:
            parts.append(f"WHERE {where_clauses}")
            params.extend(where_params)

        return " ".join(parts), params

    def _build_where(self) -> tuple[str, list[Any]]:
        """Build WHERE clause from conditions + keyword conditions."""
        clauses: list[str] = []
        params: list[Any] = []

        # Keyword conditions (exact match)
        for field, value in self.keyword_conditions.items():
            clauses.append(f"{field} = ?")
            params.append(value)

        # Operator conditions
        for cond in self.conditions:
            clause, cond_params = self._condition_to_sql(cond)
            clauses.append(clause)
            params.extend(cond_params)

        if not clauses:
            return "", []

        return " AND ".join(clauses), params

    def _condition_to_sql(self, cond: Condition | CompoundCondition) -> tuple[str, list[Any]]:
        """Convert a Condition or CompoundCondition to SQL."""
        if isinstance(cond, CompoundCondition):
            parts = []
            params: list[Any] = []
            for sub in cond.conditions:
                sql, sub_params = self._condition_to_sql(sub)
                parts.append(f"({sql})")
                params.extend(sub_params)
            join = f" {cond.join} "
            return join.join(parts), params

        # Simple condition
        if cond.op == "IN":
            placeholders = ", ".join("?" for _ in cond.value)
            return f"{cond.field} IN ({placeholders})", list(cond.value)
        elif cond.op == "LIKE":
            return f"{cond.field} LIKE ?", [cond.value]
        else:
            return f"{cond.field} {cond.op} ?", [cond.value]
