#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
动态 SQL 构建器（异步版本）
支持 PostgreSQL、MySQL、SQL Server、Oracle 的 SQL 语法差异
"""
import json
from datetime import date, datetime
from typing import Any, Dict, List, Optional, Tuple

from app.timezone import APP_TIMEZONE
from core.database_manager.sql_utils import quote_identifier as _quote_identifier
from core.database_manager.sql_utils import quote_table as _quote_table


def _normalize_db_type(db_type: str) -> str:
    normalized = (db_type or "postgresql").lower()
    if normalized in ("mssql",):
        return "sqlserver"
    if normalized in ("postgres", "psql"):
        return "postgresql"
    return normalized


class DynamicSQLBuilder:
    """动态 SQL 构建器 - 适配多种数据库"""

    def __init__(
        self,
        db_type: str,
        default_database: Optional[str] = None,
    ):
        """
        初始化 SQL 构建器

        Args:
            db_type: postgresql | mysql | sqlserver | oracle
            default_database: 连接默认库（SQL Server 与之一致时不生成三段表名）
        """
        self.db_type = _normalize_db_type(db_type)
        self.default_database = (default_database or "").strip()

    def _uses_postgres_like(self) -> bool:
        return self.db_type == "postgresql"

    def is_deleted_predicate(self) -> str:
        """未删除行的 WHERE 条件片段（不含 AND）"""
        if self.db_type == "postgresql":
            return "is_deleted = false"
        return "is_deleted = 0"

    def is_deleted_and_clause(self) -> str:
        """未删除行的 AND 条件片段"""
        return f" AND {self.is_deleted_predicate()}"

    def _like_operator(self, case_sensitive: bool) -> str:
        if self.db_type == "mysql":
            return "LIKE BINARY" if case_sensitive else "LIKE"
        if self._uses_postgres_like():
            return "LIKE" if case_sensitive else "ILIKE"
        return "LIKE"

    # ============ 标识符引用 ============

    def quote_identifier(self, name: str) -> str:
        """引用标识符（表名、列名等）"""
        return _quote_identifier(name, self.db_type)

    def _split_table_identifiers(
        self,
        table: str,
        schema: Optional[str] = None,
        database: Optional[str] = None,
    ) -> Tuple[str, Optional[str], Optional[str]]:
        """
        解析表名中的限定符，避免 SQL Server 将 schema.table 当成单段对象名。

        - MySQL: database.table（表名含点且无 database 时拆分）
        - PG / SQL Server / Oracle: schema.table
        """
        table = (table or "").strip()
        schema = (schema or "").strip() or None
        database = (database or "").strip() or None
        if not table:
            return table, schema, database

        if self.db_type == "mysql":
            if "." in table and not database:
                db_part, tbl_part = table.split(".", 1)
                if db_part.strip() and tbl_part.strip():
                    return tbl_part.strip(), schema, db_part.strip()
            return table, schema, database

        if "." in table:
            if not schema:
                sch, tbl = table.split(".", 1)
                if sch.strip() and tbl.strip():
                    return tbl.strip(), sch.strip(), database
            else:
                prefix = f"{schema}."
                if table.lower().startswith(prefix.lower()):
                    return table[len(prefix) :].strip(), schema, database
                if table.count(".") >= 1:
                    return table.rsplit(".", 1)[-1].strip(), schema, database

        return table, schema, database

    def build_table_name(
        self,
        table: str,
        schema: Optional[str] = None,
        database: Optional[str] = None,
    ) -> str:
        """
        构建完整的表名

        PostgreSQL / SQL Server / Oracle: schema.table
        MySQL: database.table
        SQL Server 可选: database.schema.table（main_table_database）
        """
        table, schema, database = self._split_table_identifiers(table, schema, database)

        if self.db_type == "mysql":
            if database:
                return (
                    f"{_quote_identifier(database, self.db_type)}"
                    f".{_quote_identifier(table, self.db_type)}"
                )
            return _quote_identifier(table, self.db_type)

        effective_schema = schema
        if self.db_type == "sqlserver" and not effective_schema:
            effective_schema = "dbo"

        table_ref = (
            _quote_table(effective_schema, table, self.db_type)
            if effective_schema
            else _quote_identifier(table, self.db_type)
        )
        if self.db_type == "sqlserver" and database:
            conn_db = self.default_database.lower()
            if conn_db and database.lower() == conn_db:
                return table_ref
            return f"{_quote_identifier(database, self.db_type)}.{table_ref}"
        return table_ref

    def _default_paging_order_by(self) -> str:
        """SQL Server / Oracle 使用 OFFSET/FETCH 时必须带 ORDER BY。"""
        if self.db_type == "sqlserver":
            return "(SELECT NULL)"
        if self.db_type == "oracle":
            return "1"
        return ""

    def _append_order_by_for_paging(self, sql: str, order_by: Optional[str]) -> str:
        if order_by:
            return f"{sql} ORDER BY {order_by}"
        if self.db_type in ("sqlserver", "oracle"):
            placeholder = self._default_paging_order_by()
            if placeholder:
                return f"{sql} ORDER BY {placeholder}"
        return sql

    def _append_limit_offset(
        self, sql: str, limit: Optional[int], offset: Optional[int]
    ) -> str:
        """追加分页子句"""
        if limit is None:
            return sql
        off = offset or 0
        if self.db_type == "sqlserver":
            return f"{sql} OFFSET {off} ROWS FETCH NEXT {limit} ROWS ONLY"
        if self.db_type == "oracle":
            if off:
                return f"{sql} OFFSET {off} ROWS FETCH NEXT {limit} ROWS ONLY"
            return f"{sql} FETCH FIRST {limit} ROWS ONLY"
        sql += f" LIMIT {limit}"
        if off:
            sql += f" OFFSET {off}"
        return sql

    # ============ 参数占位符 ============

    def get_placeholder(self, index: int = 0) -> str:
        """
        获取参数占位符

        PostgreSQL: $1, $2, ...
        MySQL: %s
        """
        if self.db_type == "postgresql":
            return f"${index + 1}"
        return "%s"

    def get_placeholders(self, count: int, start_index: int = 0) -> List[str]:
        """获取多个占位符"""
        return [self.get_placeholder(start_index + i) for i in range(count)]

    # ============ 数据类型转换 ============

    @staticmethod
    def _convert_value(value: Any) -> Any:
        """
        转换数据值为适合数据库的类型

        注意：日期时间的转换应该由 service.py 的 _convert_data_types 方法
        根据字段类型来处理，这里只处理基本的数据结构转换

        Args:
            value: 原始值

        Returns:
            转换后的值
        """
        # 处理列表和字典类型
        if isinstance(value, list):
            return json.dumps(value, ensure_ascii=False)
        if isinstance(value, dict):
            return json.dumps(value, ensure_ascii=False)

        return value

    def _build_like_expr(self, quoted_field: str, case_sensitive: bool) -> str:
        """根据数据库类型和大小写敏感标志构建 LIKE 表达式

        Args:
            quoted_field: 已引用的字段名
            case_sensitive: True=大小写敏感, False=大小写不敏感

        Returns:
            如: "CAST(name AS TEXT) LIKE :p" 或 "CAST(name AS TEXT) ILIKE :p"
        """
        if self._uses_postgres_like():
            if case_sensitive:
                return f"CAST({quoted_field} AS TEXT) LIKE :{{}}"
            return f"CAST({quoted_field} AS TEXT) ILIKE :{{}}"
        if case_sensitive:
            return f"CAST({quoted_field} AS VARCHAR(4000)) LIKE :{{}}"
        return f"LOWER(CAST({quoted_field} AS VARCHAR(4000))) LIKE LOWER(:{{}})"

    # ============ SELECT 构建 ============

    def build_select(
            self,
            table: str,
            columns: List[str] = None,
            schema: Optional[str] = None,
            database: Optional[str] = None,
            where: Optional[Dict[str, Any]] = None,
            order_by: Optional[str] = None,
            limit: Optional[int] = None,
            offset: Optional[int] = None
    ) -> Tuple[str, Dict[str, Any]]:
        """构建 SELECT 语句，返回命名参数"""
        full_table = self.build_table_name(table, schema, database)

        # 列
        if columns:
            cols = ", ".join(self.quote_identifier(c) for c in columns)
        else:
            cols = "*"

        sql = f"SELECT {cols} FROM {full_table}"
        params = {}

        # WHERE
        if where:
            where_clause, where_params = self._build_where_named(where)
            if where_clause:
                sql += f" WHERE {where_clause}"
                params.update(where_params)

        sql = self._append_order_by_for_paging(sql, order_by)
        sql = self._append_limit_offset(sql, limit, offset)

        return sql, params

    def build_count(
            self,
            table: str,
            schema: Optional[str] = None,
            database: Optional[str] = None,
            where: Optional[Dict[str, Any]] = None
    ) -> Tuple[str, Dict[str, Any]]:
        """构建 COUNT 查询，返回命名参数"""
        full_table = self.build_table_name(table, schema, database)
        sql = f"SELECT COUNT(*) as total FROM {full_table}"
        params = {}

        if where:
            where_clause, where_params = self._build_where_named(where)
            if where_clause:
                sql += f" WHERE {where_clause}"
                params.update(where_params)

        return sql, params

    def build_cursor_select(
            self,
            table: str,
            columns: List[str] = None,
            schema: Optional[str] = None,
            database: Optional[str] = None,
            where: Optional[Dict[str, Any]] = None,
            order_fields: Optional[List[Tuple[str, str]]] = None,
            cursor_values: Optional[Dict[str, Any]] = None,
            direction: str = "next",
            limit: int = 20
    ) -> Tuple[str, Dict[str, Any]]:
        """构建游标分页 SELECT 语句

        Args:
            order_fields: 排序字段列表 [(field, "ASC"|"DESC"), ...]，最后一个必须是唯一键(id)
            cursor_values: 游标值字典 {field: value, ...}，键与 order_fields 的 field 对应
            direction: 翻页方向 "next"(下一页) 或 "prev"(上一页)
            limit: 每页条数（内部会 +1 来判断 has_more）
        """
        full_table = self.build_table_name(table, schema, database)

        if columns:
            cols = ", ".join(self.quote_identifier(c) for c in columns)
        else:
            cols = "*"

        sql = f"SELECT {cols} FROM {full_table}"
        params = {}

        where_clauses = []

        # 先构建业务 WHERE 条件
        if where:
            biz_clause, biz_params = self._build_where_named(where)
            if biz_clause:
                where_clauses.append(biz_clause)
                params.update(biz_params)

        # 构建游标 WHERE 条件（行值比较）
        if cursor_values and order_fields:
            cursor_clause, cursor_params = self._build_cursor_where(
                order_fields, cursor_values, direction
            )
            if cursor_clause:
                where_clauses.append(cursor_clause)
                params.update(cursor_params)

        if where_clauses:
            sql += f" WHERE {' AND '.join(where_clauses)}"

        # 构建 ORDER BY（prev 方向需要反转排序）
        order_by = None
        if order_fields:
            order_parts = []
            for field, raw_dir in order_fields:
                if direction == "prev":
                    actual_dir = "ASC" if raw_dir.upper() == "DESC" else "DESC"
                else:
                    actual_dir = raw_dir.upper()
                order_parts.append(f"{self.quote_identifier(field)} {actual_dir}")
            order_by = ", ".join(order_parts)

        sql = self._append_order_by_for_paging(sql, order_by)
        sql = self._append_limit_offset(sql, limit + 1, 0)

        return sql, params

    def _cursor_comparison_op(
            self,
            fields_with_values: List[Tuple[str, str]],
            direction: str,
    ) -> str:
        """游标翻页比较符（与首列排序方向、翻页方向一致）。"""
        first_dir = fields_with_values[0][1].upper()
        if direction == "prev":
            return ">" if first_dir == "DESC" else "<"
        return "<" if first_dir == "DESC" else ">"

    def _build_cursor_where_lexographic(
            self,
            fields_with_values: List[Tuple[str, str]],
            cursor_values: Dict[str, Any],
            direction: str,
    ) -> Tuple[str, Dict[str, Any]]:
        """SQL Server / Oracle：等值前缀 + OR 链，避免行值比较语法不兼容。"""
        op = self._cursor_comparison_op(fields_with_values, direction)
        params: Dict[str, Any] = {}
        or_parts: List[str] = []

        for i, (field, _) in enumerate(fields_with_values):
            conjuncts: List[str] = []
            for j in range(i):
                prev_field = fields_with_values[j][0]
                eq_name = f"cursor_eq_{i}_{j}"
                conjuncts.append(
                    f"{self.quote_identifier(prev_field)} = :{eq_name}"
                )
                params[eq_name] = cursor_values[prev_field]
            cmp_name = f"cursor_cmp_{i}"
            conjuncts.append(
                f"{self.quote_identifier(field)} {op} :{cmp_name}"
            )
            params[cmp_name] = cursor_values[field]
            or_parts.append(f"({' AND '.join(conjuncts)})")

        return f"({' OR '.join(or_parts)})", params

    def _build_cursor_where(
            self,
            order_fields: List[Tuple[str, str]],
            cursor_values: Dict[str, Any],
            direction: str
    ) -> Tuple[str, Dict[str, Any]]:
        """构建游标 WHERE 条件。

        PostgreSQL / MySQL：行值比较 (a, b) > (:a, :b)
        SQL Server / Oracle：等值前缀 + OR 链
        """
        fields_with_values = [
            (f, d) for f, d in order_fields if f in cursor_values
        ]
        if not fields_with_values:
            return "", {}

        if self.db_type in ("sqlserver", "oracle"):
            return self._build_cursor_where_lexographic(
                fields_with_values, cursor_values, direction
            )

        params = {}
        quoted_fields = []
        param_placeholders = []

        for i, (field, _) in enumerate(fields_with_values):
            quoted_fields.append(self.quote_identifier(field))
            param_name = f"cursor_{i}"
            param_placeholders.append(f":{param_name}")
            params[param_name] = cursor_values[field]

        op = self._cursor_comparison_op(fields_with_values, direction)
        lhs = f"({', '.join(quoted_fields)})"
        rhs = f"({', '.join(param_placeholders)})"
        clause = f"{lhs} {op} {rhs}"

        return clause, params

    # ============ INSERT 构建 ============

    def build_insert(
            self,
            table: str,
            data: Dict[str, Any],
            schema: Optional[str] = None,
            database: Optional[str] = None,
            return_id: bool = True
    ) -> Tuple[str, Dict[str, Any]]:
        """构建 INSERT 语句，返回 SQL 和命名参数字典"""
        full_table = self.build_table_name(table, schema, database)

        # 清理字段名中的空格，并转换数据类型
        cleaned_data = {}

        for key, value in data.items():
            cleaned_key = key.strip()
            # 转换日期时间字符串为 Python 对象
            converted_value = self._convert_value(value)
            cleaned_data[cleaned_key] = converted_value

        columns = list(cleaned_data.keys())

        cols = ", ".join(self.quote_identifier(c) for c in columns)
        # 使用命名参数 :param_name（使用清理后的字段名）
        placeholders = ", ".join(f":{col}" for col in columns)

        sql = f"INSERT INTO {full_table} ({cols}) VALUES ({placeholders})"

        # 返回自增 ID
        if return_id and self.db_type == "postgresql":
            sql += " RETURNING id"

        return sql, cleaned_data

    def build_batch_insert(
            self,
            table: str,
            columns: List[str],
            rows: List[List[Any]],
            schema: Optional[str] = None,
            database: Optional[str] = None
    ) -> Tuple[str, List[Any]]:
        """构建批量 INSERT 语句"""
        full_table = self.build_table_name(table, schema, database)
        cols = ", ".join(self.quote_identifier(c) for c in columns)

        # 构建多行 VALUES
        row_placeholders = []
        params = []
        param_index = 0

        for row in rows:
            placeholders = ", ".join(self.get_placeholders(len(row), param_index))
            row_placeholders.append(f"({placeholders})")
            params.extend(row)
            param_index += len(row)

        sql = f"INSERT INTO {full_table} ({cols}) VALUES {', '.join(row_placeholders)}"
        return sql, params

    def build_batch_insert_named(
            self,
            table: str,
            data_list: List[Dict[str, Any]],
            schema: Optional[str] = None,
            database: Optional[str] = None
    ) -> Tuple[str, Any]:
        """
        构建批量 INSERT 语句

        Args:
            table: 表名
            data_list: 数据列表，每个元素是一个字典
            schema: Schema 名
            database: 数据库名

        Returns:
            PostgreSQL: (SQL 语句, 位置参数列表)
            MySQL: (SQL 语句, 命名参数字典)
        """
        if not data_list:
            raise ValueError("data_list 不能为空")

        full_table = self.build_table_name(table, schema, database)

        # 获取所有列名（使用第一条数据的键）
        columns = list(data_list[0].keys())
        cols = ", ".join(self.quote_identifier(c) for c in columns)

        if self.db_type == "postgresql":
            # PostgreSQL 使用位置参数 $1, $2, ...
            row_placeholders = []
            params = []
            param_index = 0

            for data in data_list:
                placeholders = []
                for col in columns:
                    placeholders.append(f"${param_index + 1}")
                    # 转换数据类型并添加到参数列表
                    params.append(self._convert_value(data.get(col)))
                    param_index += 1
                row_placeholders.append(f"({', '.join(placeholders)})")

            sql = f"INSERT INTO {full_table} ({cols}) VALUES {', '.join(row_placeholders)}"
            return sql, params
        else:
            # MySQL 使用命名参数
            row_placeholders = []
            params = {}

            for row_idx, data in enumerate(data_list):
                placeholders = []
                for col in columns:
                    param_name = f"p{row_idx}_{col}"
                    placeholders.append(f":{param_name}")
                    # 转换数据类型
                    params[param_name] = self._convert_value(data.get(col))
                row_placeholders.append(f"({', '.join(placeholders)})")

            sql = f"INSERT INTO {full_table} ({cols}) VALUES {', '.join(row_placeholders)}"
            return sql, params

    # ============ UPDATE 构建 ============

    def build_update(
            self,
            table: str,
            data: Dict[str, Any],
            pk_field: str,
            pk_value: Any,
            schema: Optional[str] = None,
            database: Optional[str] = None
    ) -> Tuple[str, Dict[str, Any]]:
        """构建 UPDATE 语句，返回 SQL 和命名参数字典"""
        full_table = self.build_table_name(table, schema, database)

        set_clauses = []
        params = {}
        param_counter = 0

        # 清理字段名中的空格，并转换数据类型
        for col, val in data.items():
            cleaned_col = col.strip()
            # 转换日期时间字符串为 Python 对象
            converted_val = self._convert_value(val)
            param_name = f"param_{param_counter}"
            set_clauses.append(f"{self.quote_identifier(cleaned_col)} = :{param_name}")
            params[param_name] = converted_val
            param_counter += 1

        # 清理主键字段名
        cleaned_pk_field = pk_field.strip()
        pk_param_name = f"param_{param_counter}"
        sql = f"UPDATE {full_table} SET {', '.join(set_clauses)} WHERE {self.quote_identifier(cleaned_pk_field)} = :{pk_param_name}"
        params[pk_param_name] = pk_value

        return sql, params

    # ============ DELETE 构建 ============

    def build_delete(
            self,
            table: str,
            pk_field: str,
            pk_value: Any,
            schema: Optional[str] = None,
            database: Optional[str] = None
    ) -> Tuple[str, Dict[str, Any]]:
        """构建 DELETE 语句，返回 SQL 和命名参数字典"""
        full_table = self.build_table_name(table, schema, database)
        sql = f"DELETE FROM {full_table} WHERE {self.quote_identifier(pk_field)} = :pk_value"
        return sql, {"pk_value": pk_value}

    def build_delete_by_foreign_key(
            self,
            table: str,
            fk_field: str,
            fk_value: Any,
            schema: Optional[str] = None,
            database: Optional[str] = None
    ) -> Tuple[str, Dict[str, Any]]:
        """根据外键删除，返回 SQL 和命名参数字典"""
        full_table = self.build_table_name(table, schema, database)
        sql = f"DELETE FROM {full_table} WHERE {self.quote_identifier(fk_field)} = :fk_value"
        return sql, {"fk_value": fk_value}

    # ============ WHERE 条件构建 ============

    def _build_where_named(self, conditions: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
        """构建 WHERE 子句（使用命名参数）"""
        clauses = []
        params = {}
        param_counter = 0

        for field, condition in conditions.items():
            if condition is None:
                continue

            # 处理多字段搜索（OR 关系）
            if field == '__search__' and isinstance(condition, dict):
                keyword = condition.get('keyword')
                search_fields = condition.get('fields', [])
                if keyword and search_fields:
                    search_clauses = []
                    for search_field in search_fields:
                        quoted_search_field = self.quote_identifier(search_field)
                        param_name = f"search_param_{param_counter}"
                        # 使用 ILIKE（PostgreSQL）或 LIKE（其他数据库）进行不区分大小写的模糊搜索
                        if self._uses_postgres_like():
                            search_clauses.append(
                                f"CAST({quoted_search_field} AS TEXT) ILIKE :{param_name}"
                            )
                        else:
                            search_clauses.append(
                                f"LOWER(CAST({quoted_search_field} AS VARCHAR(4000))) "
                                f"LIKE LOWER(:{param_name})"
                            )
                        params[param_name] = f"%{keyword}%"
                        param_counter += 1
                    if search_clauses:
                        clauses.append(f"({' OR '.join(search_clauses)})")
                continue

            quoted_field = self.quote_identifier(field)

            if isinstance(condition, dict):
                cond_type = condition.get("type", "eq")
                value = condition.get("value")

                if cond_type == "like" and value:
                    case_sensitive = condition.get("case_sensitive", True)
                    param_name = f"param_{param_counter}"
                    like_expr = self._build_like_expr(quoted_field, case_sensitive)
                    clauses.append(like_expr.format(param_name))
                    params[param_name] = f"%{value}%"
                    param_counter += 1
                elif cond_type == "eq" and value is not None:
                    param_name = f"param_{param_counter}"
                    case_sensitive = condition.get("case_sensitive", True)
                    if case_sensitive:
                        clauses.append(f"{quoted_field} = :{param_name}")
                    else:
                        clauses.append(f"LOWER({quoted_field}) = LOWER(:{param_name})")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "ne" and value is not None:
                    param_name = f"param_{param_counter}"
                    clauses.append(f"{quoted_field} != :{param_name}")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "gt" and value is not None:
                    param_name = f"param_{param_counter}"
                    clauses.append(f"{quoted_field} > :{param_name}")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "gte" and value is not None:
                    param_name = f"param_{param_counter}"
                    clauses.append(f"{quoted_field} >= :{param_name}")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "lt" and value is not None:
                    param_name = f"param_{param_counter}"
                    clauses.append(f"{quoted_field} < :{param_name}")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "lte" and value is not None:
                    param_name = f"param_{param_counter}"
                    clauses.append(f"{quoted_field} <= :{param_name}")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "range" and value and isinstance(value, list) and len(value) == 2:
                    param_name_start = f"param_{param_counter}"
                    param_name_end = f"param_{param_counter + 1}"
                    clauses.append(f"{quoted_field} BETWEEN :{param_name_start} AND :{param_name_end}")
                    params[param_name_start] = value[0]
                    params[param_name_end] = value[1]
                    param_counter += 2
                elif cond_type == "in" and value:
                    # IN 条件：value 是一个列表
                    if isinstance(value, list) and len(value) > 0:
                        placeholders = []
                        for v in value:
                            param_name = f"param_{param_counter}"
                            placeholders.append(f":{param_name}")
                            params[param_name] = v
                            param_counter += 1
                        clauses.append(f"{quoted_field} IN ({', '.join(placeholders)})")
                elif cond_type == "eq_or_null" and value is not None:
                    # 等值 OR 字段为空（用于数据权限：字段为空时所有人可见）
                    param_name = f"param_{param_counter}"
                    clauses.append(f"({quoted_field} = :{param_name} OR {quoted_field} IS NULL)")
                    params[param_name] = value
                    param_counter += 1
                elif cond_type == "in_or_null" and value:
                    # IN 条件 OR 字段为空（用于数据权限：字段为空时所有人可见）
                    if isinstance(value, list) and len(value) > 0:
                        placeholders = []
                        for v in value:
                            param_name = f"param_{param_counter}"
                            placeholders.append(f":{param_name}")
                            params[param_name] = v
                            param_counter += 1
                        clauses.append(f"({quoted_field} IN ({', '.join(placeholders)}) OR {quoted_field} IS NULL)")
                elif cond_type == "space_like_and" and value:
                    # 空格模糊且：按空格拆分关键词，用 AND + LIKE 连接（范围逐渐缩小）
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            param_name = f"param_{param_counter}"
                            like_expr = self._build_like_expr(quoted_field, case_sensitive)
                            keyword_clauses.append(like_expr.format(param_name))
                            params[param_name] = f"%{keyword}%"
                            param_counter += 1
                        clauses.append(f"({' AND '.join(keyword_clauses)})")
                elif cond_type == "space_like_or" and value:
                    # 空格模糊或：按空格拆分关键词，用 OR + LIKE 连接（范围逐渐扩大）
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            param_name = f"param_{param_counter}"
                            like_expr = self._build_like_expr(quoted_field, case_sensitive)
                            keyword_clauses.append(like_expr.format(param_name))
                            params[param_name] = f"%{keyword}%"
                            param_counter += 1
                        clauses.append(f"({' OR '.join(keyword_clauses)})")
                elif cond_type == "space_eq_and" and value:
                    # 空格精确且：按空格拆分关键词，用 AND + = 连接（范围逐渐缩小）
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            param_name = f"param_{param_counter}"
                            if case_sensitive:
                                keyword_clauses.append(f"{quoted_field} = :{param_name}")
                            else:
                                keyword_clauses.append(f"LOWER({quoted_field}) = LOWER(:{param_name})")
                            params[param_name] = keyword
                            param_counter += 1
                        clauses.append(f"({' AND '.join(keyword_clauses)})")
                elif cond_type == "space_eq_or" and value:
                    # 空格精确或：按空格拆分关键词，用 OR + = 连接（范围逐渐扩大）
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            param_name = f"param_{param_counter}"
                            if case_sensitive:
                                keyword_clauses.append(f"{quoted_field} = :{param_name}")
                            else:
                                keyword_clauses.append(f"LOWER({quoted_field}) = LOWER(:{param_name})")
                            params[param_name] = keyword
                            param_counter += 1
                        clauses.append(f"({' OR '.join(keyword_clauses)})")
                elif cond_type == "null":
                    clauses.append(f"{quoted_field} IS NULL")
                elif cond_type == "not_null":
                    clauses.append(f"{quoted_field} IS NOT NULL")
            else:
                # 简单等值条件
                if condition is not None and condition != "":
                    param_name = f"param_{param_counter}"
                    clauses.append(f"{quoted_field} = :{param_name}")
                    params[param_name] = condition
                    param_counter += 1

        return " AND ".join(clauses), params

    def _build_where(self, conditions: Dict[str, Any], start_index: int = 0) -> Tuple[str, List[Any], int]:
        """
        构建 WHERE 子句

        支持的条件格式:
        - {"field": value}  -> field = value
        - {"field": {"type": "like", "value": "xxx"}}  -> field LIKE '%xxx%'
        - {"field": {"type": "eq", "value": xxx}}  -> field = xxx
        - {"field": {"type": "ne", "value": xxx}}  -> field != xxx
        - {"field": {"type": "gt", "value": xxx}}  -> field > xxx
        - {"field": {"type": "gte", "value": xxx}}  -> field >= xxx
        - {"field": {"type": "lt", "value": xxx}}  -> field < xxx
        - {"field": {"type": "lte", "value": xxx}}  -> field <= xxx
        - {"field": {"type": "in", "value": [...]}}  -> field IN (...)
        - {"field": {"type": "range", "value": [start, end]}}  -> field BETWEEN start AND end
        - {"field": {"type": "null"}}  -> field IS NULL
        - {"field": {"type": "not_null"}}  -> field IS NOT NULL
        """
        clauses = []
        params = []
        param_index = start_index

        for field, condition in conditions.items():
            quoted_field = self.quote_identifier(field)

            if condition is None:
                continue

            if isinstance(condition, dict):
                cond_type = condition.get("type", "eq")
                value = condition.get("value")

                if cond_type == "like" and value:
                    case_sensitive = condition.get("case_sensitive", True)
                    if self.db_type == "postgresql":
                        like_op = "LIKE" if case_sensitive else "ILIKE"
                        clauses.append(f"CAST({quoted_field} AS TEXT) {like_op} {self.get_placeholder(param_index)}")
                    else:
                        like_op = self._like_operator(case_sensitive)
                        cast_type = "VARCHAR(4000)" if self.db_type in ("sqlserver", "oracle") else "CHAR"
                        clauses.append(
                            f"CAST({quoted_field} AS {cast_type}) {like_op} "
                            f"{self.get_placeholder(param_index)}"
                        )
                    params.append(f"%{value}%")
                    param_index += 1
                elif cond_type == "eq" and value is not None:
                    case_sensitive = condition.get("case_sensitive", True)
                    if case_sensitive:
                        clauses.append(f"{quoted_field} = {self.get_placeholder(param_index)}")
                    else:
                        clauses.append(f"LOWER({quoted_field}) = LOWER({self.get_placeholder(param_index)})")
                    params.append(value)
                    param_index += 1
                elif cond_type == "ne" and value is not None:
                    clauses.append(f"{quoted_field} != {self.get_placeholder(param_index)}")
                    params.append(value)
                    param_index += 1
                elif cond_type == "gt" and value is not None:
                    clauses.append(f"{quoted_field} > {self.get_placeholder(param_index)}")
                    params.append(value)
                    param_index += 1
                elif cond_type == "gte" and value is not None:
                    clauses.append(f"{quoted_field} >= {self.get_placeholder(param_index)}")
                    params.append(value)
                    param_index += 1
                elif cond_type == "lt" and value is not None:
                    clauses.append(f"{quoted_field} < {self.get_placeholder(param_index)}")
                    params.append(value)
                    param_index += 1
                elif cond_type == "lte" and value is not None:
                    clauses.append(f"{quoted_field} <= {self.get_placeholder(param_index)}")
                    params.append(value)
                    param_index += 1
                elif cond_type == "in" and value:
                    placeholders = ", ".join(self.get_placeholders(len(value), param_index))
                    clauses.append(f"{quoted_field} IN ({placeholders})")
                    params.extend(value)
                    param_index += len(value)
                elif cond_type == "range" and value and len(value) == 2:
                    clauses.append(f"{quoted_field} BETWEEN {self.get_placeholder(param_index)} AND {self.get_placeholder(param_index + 1)}")
                    params.extend(value)
                    param_index += 2
                elif cond_type == "space_like_and" and value:
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            if self.db_type == "postgresql":
                                like_op = "LIKE" if case_sensitive else "ILIKE"
                                keyword_clauses.append(f"CAST({quoted_field} AS TEXT) {like_op} {self.get_placeholder(param_index)}")
                            else:
                                like_op = self._like_operator(case_sensitive)
                                cast_type = "VARCHAR(4000)" if self.db_type in ("sqlserver", "oracle") else "CHAR"
                                keyword_clauses.append(
                                    f"CAST({quoted_field} AS {cast_type}) {like_op} "
                                    f"{self.get_placeholder(param_index)}"
                                )
                            params.append(f"%{keyword}%")
                            param_index += 1
                        clauses.append(f"({' AND '.join(keyword_clauses)})")
                elif cond_type == "space_like_or" and value:
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            if self.db_type == "postgresql":
                                like_op = "LIKE" if case_sensitive else "ILIKE"
                                keyword_clauses.append(f"CAST({quoted_field} AS TEXT) {like_op} {self.get_placeholder(param_index)}")
                            else:
                                like_op = self._like_operator(case_sensitive)
                                cast_type = "VARCHAR(4000)" if self.db_type in ("sqlserver", "oracle") else "CHAR"
                                keyword_clauses.append(
                                    f"CAST({quoted_field} AS {cast_type}) {like_op} "
                                    f"{self.get_placeholder(param_index)}"
                                )
                            params.append(f"%{keyword}%")
                            param_index += 1
                        clauses.append(f"({' OR '.join(keyword_clauses)})")
                elif cond_type == "space_eq_and" and value:
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            if case_sensitive:
                                keyword_clauses.append(f"{quoted_field} = {self.get_placeholder(param_index)}")
                            else:
                                keyword_clauses.append(f"LOWER({quoted_field}) = LOWER({self.get_placeholder(param_index)})")
                            params.append(keyword)
                            param_index += 1
                        clauses.append(f"({' AND '.join(keyword_clauses)})")
                elif cond_type == "space_eq_or" and value:
                    case_sensitive = condition.get("case_sensitive", True)
                    keywords = str(value).split()
                    if keywords:
                        keyword_clauses = []
                        for keyword in keywords:
                            if case_sensitive:
                                keyword_clauses.append(f"{quoted_field} = {self.get_placeholder(param_index)}")
                            else:
                                keyword_clauses.append(f"LOWER({quoted_field}) = LOWER({self.get_placeholder(param_index)})")
                            params.append(keyword)
                            param_index += 1
                        clauses.append(f"({' OR '.join(keyword_clauses)})")
                elif cond_type == "null":
                    clauses.append(f"{quoted_field} IS NULL")
                elif cond_type == "not_null":
                    clauses.append(f"{quoted_field} IS NOT NULL")
            else:
                # 简单等值条件
                if condition is not None and condition != "":
                    clauses.append(f"{quoted_field} = {self.get_placeholder(param_index)}")
                    params.append(condition)
                    param_index += 1

        return " AND ".join(clauses), params, param_index
