from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any


# ------------------------------------------------------
# Expressions
# ------------------------------------------------------


@dataclass(frozen=True)
class SQL2Expr:
    pass


@dataclass(frozen=True)
class SQL2ColumnExpr(SQL2Expr):
    table_alias: str
    column_name: str


@dataclass(frozen=True)
class SQL2LiteralExpr(SQL2Expr):
    value: Any


@dataclass(frozen=True)
class SQL2NullExpr(SQL2Expr):
    pass


@dataclass(frozen=True)
class SQL2FunctionExpr(SQL2Expr):
    name: str
    args: tuple[SQL2Expr, ...] = field(default_factory=tuple)
    distinct: bool = False


@dataclass(frozen=True)
class SQL2OrderByExpr:
    expr: SQL2Expr
    descending: bool = False


@dataclass(frozen=True)
class SQL2WindowExpr(SQL2Expr):
    name: str
    args: tuple[SQL2Expr, ...] = field(default_factory=tuple)
    partition_by: tuple[SQL2Expr, ...] = field(default_factory=tuple)
    order_by: tuple[SQL2OrderByExpr, ...] = field(default_factory=tuple)
    distinct: bool = False


@dataclass(frozen=True)
class SQL2CastExpr(SQL2Expr):
    expr: SQL2Expr
    target_type: str


@dataclass(frozen=True)
class SQL2BinaryExpr(SQL2Expr):
    op: str
    left: SQL2Expr
    right: SQL2Expr


@dataclass(frozen=True)
class SQL2ExistsExpr(SQL2Expr):
    query: SQL2Query
    negated: bool = False


# ------------------------------------------------------
# Query shape
# ------------------------------------------------------


@dataclass(frozen=True)
class SQL2SelectItem:
    expr: SQL2Expr
    alias: str | None = None


@dataclass(frozen=True)
class SQL2TableSource:
    name: str


@dataclass(frozen=True)
class SQL2SubquerySource:
    query: SQL2Query


SQL2Source = SQL2TableSource | SQL2SubquerySource


@dataclass(frozen=True)
class SQL2FromItem:
    source: SQL2Source
    alias: str
    lateral: bool = False


@dataclass(frozen=True)
class SQL2Join:
    source: SQL2FromItem
    join_type: str = "INNER"
    on: tuple[SQL2Expr, ...] = field(default_factory=tuple)


@dataclass(frozen=True)
class SQL2SelectQuery:
    select_items: tuple[SQL2SelectItem, ...] = field(default_factory=tuple)
    from_item: SQL2FromItem | None = None
    joins: tuple[SQL2Join, ...] = field(default_factory=tuple)
    where: tuple[SQL2Expr, ...] = field(default_factory=tuple)
    group_by: tuple[SQL2Expr, ...] = field(default_factory=tuple)
    having: tuple[SQL2Expr, ...] = field(default_factory=tuple)
    distinct: bool = False


@dataclass(frozen=True)
class SQL2UnionQuery:
    parts: tuple[SQL2Query, ...] = field(default_factory=tuple)
    distinct: bool = True


@dataclass(frozen=True)
class SQL2RecursiveQuery:
    cte_name: str
    column_names: tuple[str, ...] = field(default_factory=tuple)
    base_query: SQL2Query | None = None
    recursive_query: SQL2Query | None = None
    final_query: SQL2SelectQuery | None = None
    distinct: bool = False


SQL2Query = SQL2SelectQuery | SQL2UnionQuery | SQL2RecursiveQuery


# ------------------------------------------------------
# Program operations
# ------------------------------------------------------


@dataclass(frozen=True)
class SQL2CreateViewOp:
    name: str
    query: SQL2Query


@dataclass(frozen=True)
class SQL2SemiNaiveTable:
    table_name: str
    columns: tuple[str, ...]
    base_query: SQL2Query
    delta_query: SQL2Query
    merge_query: SQL2Query
    final_query: SQL2Query
    accum_view_name: str
    delta_view_name: str
    next_delta_view_name: str


@dataclass(frozen=True)
class SQL2SemiNaiveLoop:
    tables: tuple[SQL2SemiNaiveTable, ...] = field(default_factory=tuple)


@dataclass(frozen=True)
class SQL2StratumProgram:
    stratum_id: int
    operations: tuple[SQL2CreateViewOp, ...] = field(default_factory=tuple)
    semi_naive_loops: tuple[SQL2SemiNaiveLoop, ...] = field(default_factory=tuple)


@dataclass(frozen=True)
class SQL2ProgramIR:
    strata: tuple[SQL2StratumProgram, ...] = field(default_factory=tuple)


# ------------------------------------------------------
# Execution plan
# ------------------------------------------------------


@dataclass
class SQL2ExecutionStep:
    views: list[SQL2CreateViewOp] = field(default_factory=list)
    semi_naive_loops: list[SQL2SemiNaiveLoop | Any] = field(default_factory=list)


@dataclass
class SQL2ExecutionPlan:
    steps: list[SQL2ExecutionStep] = field(default_factory=list)


ExecutionPlan = SQL2ExecutionPlan
ExecutionStep = SQL2ExecutionStep

__all__ = ["SQL2ExecutionPlan", "SQL2ExecutionStep", "ExecutionPlan", "ExecutionStep"]
