"""
异步基础仓库模式实现

提供通用的异步 CRUD 操作，支持过滤、排序和分页。
所有异步仓库应继承自 AsyncBaseRepository 以确保一致的数据访问模式。
"""

from typing import Generic, TypeVar, Type, Optional, List, Dict, Any, Tuple
from sqlalchemy import asc, desc, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy.orm import selectinload
import logging
import time

from sqlmodel import SQLModel

logger = logging.getLogger(__name__)

# 泛型 type for the model
T = TypeVar("T", bound=SQLModel)


class AsyncBaseRepository(Generic[T]):
    """
    异步泛型基础仓库，提供通用的异步 CRUD 操作。

    特性:
    - 通用 CRUD 操作 (Create, Read, Update, Delete)
    - 支持多种操作符的过滤
    - 支持排序 (升序/降序)
    - 支持分页
    - 支持软删除
    - 计数操作
    - 批量操作

    用法:
        class ProjectRepository(AsyncBaseRepository[ProjectDB]):
            def __init__(self, session: AsyncSession):
                super().__init__(ProjectDB, session)
    """

    def __init__(self, model: Type[T], session: AsyncSession):
        """
        使用模型类和数据库会话初始化仓库。

        Args:
            model: 此仓库的 SQLModel 类
            session: 异步数据库会话
        """
        self.model = model
        self.session = session
        self._query_count = 0
        self._total_query_time = 0.0

    def get_query_stats(self) -> Dict[str, Any]:
        """
        获取此仓库实例的查询统计信息。

        Returns:
            包含查询次数和总时间的字典
        """
        return {
            "query_count": self._query_count,
            "total_query_time": self._total_query_time,
            "avg_query_time": self._total_query_time / self._query_count if self._query_count > 0 else 0
        }

    async def get(self, id: str) -> Optional[T]:
        """
        通过 ID 获取单条记录。

        Args:
            id: 主键值

        Returns:
            模型实例，如果未找到则返回 None
        """
        start_time = time.time()
        result = await self.session.get(self.model, id)

        elapsed = time.time() - start_time
        self._query_count += 1
        self._total_query_time += elapsed

        return result

    async def get_with_relations(self, id: str, eager_load: List[str]) -> Optional[T]:
        """
        通过 ID 获取单条记录并预加载关联关系。
        防止访问关联数据时出现 N+1 查询问题。

        Args:
            id: 主键值
            eager_load: 要预加载的关联关系名称列表

        Returns:
            模型实例，如果未找到则返回 None
        """
        start_time = time.time()

        pk_column = self.model.__table__.primary_key.columns.keys()[0]
        statement = select(self.model).where(
            getattr(self.model, pk_column) == id
        )

        # 应用预加载
        for relationship in eager_load:
            if hasattr(self.model, relationship):
                statement = statement.options(selectinload(getattr(self.model, relationship)))
            else:
                logger.warning(f"Relationship {relationship} not found in {self.model.__name__}")

        result = (await self.session.execute(statement)).scalar_one_or_none()

        elapsed = time.time() - start_time
        self._query_count += 1
        self._total_query_time += elapsed

        if elapsed > 1.0:
            logger.warning(
                f"Slow query in {self.model.__name__}.get_with_relations(): {elapsed:.2f}s",
                extra={"query_time": elapsed, "model": self.model.__name__, "id": id}
            )

        return result

    async def get_by_field(self, field_name: str, value: Any) -> Optional[T]:
        """
        通过特定字段值获取单条记录。

        Args:
            field_name: 要过滤的字段名称
            value: 要匹配的值

        Returns:
            第一个匹配的模型实例，如果没有则返回 None
        """
        statement = select(self.model).where(getattr(self.model, field_name) == value)
        result = await self.session.execute(statement)
        return result.scalar_one_or_none()

    async def list(
        self,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None,
        sort_by: Optional[str] = None,
        sort_order: str = "asc",
        eager_load: Optional[List[str]] = None
    ) -> List[T]:
        """
        列出记录，支持可选的过滤、排序和分页。

        Args:
            skip: 跳过的记录数（偏移量）
            limit: 返回的最大记录数
            filters: 用于过滤的 field:value 字典
            sort_by: 要排序的字段名
            sort_order: 排序顺序 ('asc' 或 'desc')
            eager_load: 要预加载的关联关系名称列表（防止 N+1 查询）

        Returns:
            模型实例列表
        """
        start_time = time.time()

        statement = select(self.model)

        # 应用预加载防止 N+1 查询
        if eager_load:
            for relationship in eager_load:
                if hasattr(self.model, relationship):
                    statement = statement.options(selectinload(getattr(self.model, relationship)))
                else:
                    logger.warning(f"Relationship {relationship} not found in {self.model.__name__}")

        # 应用过滤
        if filters:
            statement = self._apply_filters(statement, filters)

        # 应用排序
        if sort_by:
            sort_column = getattr(self.model, sort_by)
            if sort_order.lower() == "desc":
                statement = statement.order_by(desc(sort_column))
            else:
                statement = statement.order_by(asc(sort_column))

        # 应用分页
        statement = statement.offset(skip).limit(limit)

        result = await self.session.execute(statement)
        records = list(result.scalars().all())

        # 跟踪查询性能
        elapsed = time.time() - start_time
        self._query_count += 1
        self._total_query_time += elapsed

        if elapsed > 1.0:
            logger.warning(
                f"Slow query in {self.model.__name__}.list(): {elapsed:.2f}s",
                extra={
                    "query_time": elapsed,
                    "model": self.model.__name__,
                    "filters": filters,
                    "limit": limit
                }
            )

        return records

    async def list_paginated(
        self,
        page: int = 1,
        page_size: int = 20,
        filters: Optional[Dict[str, Any]] = None,
        sort_by: Optional[str] = None,
        sort_order: str = "asc",
        eager_load: Optional[List[str]] = None
    ) -> Tuple[List[T], int]:
        """
        列出记录并返回分页元数据。

        Args:
            page: 页码（从 1 开始）
            page_size: 每页记录数
            filters: 用于过滤的 field:value 字典
            sort_by: 要排序的字段名
            sort_order: 排序顺序 ('asc' 或 'desc')
            eager_load: 要预加载的关联关系名称列表（防止 N+1 查询）

        Returns:
            (records, total_count) 元组
        """
        # 计算偏移量
        skip = (page - 1) * page_size

        # 获取记录
        records = await self.list(
            skip=skip,
            limit=page_size,
            filters=filters,
            sort_by=sort_by,
            sort_order=sort_order,
            eager_load=eager_load
        )

        # 获取总数
        total = await self.count(filters=filters)

        return records, total

    async def count(self, filters: Optional[Dict[str, Any]] = None) -> int:
        """
        计数记录，支持可选的过滤。

        Args:
            filters: 用于过滤的 field:value 字典

        Returns:
            匹配记录的数量
        """
        start_time = time.time()

        statement = select(func.count()).select_from(self.model)

        if filters:
            statement = self._apply_filters(statement, filters)

        result = await self.session.execute(statement)
        count = result.scalar_one()

        # 跟踪查询性能
        elapsed = time.time() - start_time
        self._query_count += 1
        self._total_query_time += elapsed

        return count

    async def create(self, obj: T) -> T:
        """
        创建新记录。

        Args:
            obj: 要创建的模型实例

        Returns:
            已创建的模型实例（包含更新的字段）
        """
        self.session.add(obj)
        await self.session.commit()
        await self.session.refresh(obj)
        return obj

    async def create_many(self, objects: List[T]) -> List[T]:
        """
        在单个事务中创建多条记录。

        Args:
            objects: 要创建的模型实例列表

        Returns:
            已创建的模型实例列表
        """
        for obj in objects:
            self.session.add(obj)
        await self.session.commit()

        for obj in objects:
            await self.session.refresh(obj)

        return objects

    async def update(self, obj: T) -> T:
        """
        更新现有记录。

        Args:
            obj: 包含更新值的模型实例

        Returns:
            更新后的模型实例
        """
        self.session.add(obj)
        await self.session.commit()
        await self.session.refresh(obj)
        return obj

    async def update_by_id(self, id: str, data: Dict[str, Any]) -> Optional[T]:
        """
        通过 ID 使用部分数据更新记录。

        Args:
            id: 主键值
            data: 要更新的字段字典

        Returns:
            更新后的模型实例，如果未找到则返回 None
        """
        obj = await self.get(id)
        if not obj:
            return None

        for key, value in data.items():
            if hasattr(obj, key):
                setattr(obj, key, value)

        return await self.update(obj)

    async def delete(self, id: str) -> bool:
        """
        通过 ID 删除记录（硬删除）。

        Args:
            id: 主键值

        Returns:
            如果删除成功返回 True，如果未找到返回 False
        """
        obj = await self.get(id)
        if not obj:
            return False

        await self.session.delete(obj)
        await self.session.commit()
        return True

    async def soft_delete(self, id: str) -> bool:
        """
        通过设置 deleted_at 时间戳软删除记录。
        仅适用于具有 deleted_at 字段的模型。

        Args:
            id: 主键值

        Returns:
            如果软删除成功返回 True，如果未找到或没有 deleted_at 字段返回 False
        """
        obj = await self.get(id)
        if not obj:
            return False

        if not hasattr(obj, "deleted_at"):
            logger.warning(f"Model {self.model.__name__} does not support soft delete")
            return False

        from datetime import datetime
        obj.deleted_at = datetime.now().timestamp()
        await self.update(obj)
        return True

    async def exists(self, id: str) -> bool:
        """
        通过 ID 检查记录是否存在。

        Args:
            id: 主键值

        Returns:
            如果存在返回 True，否则返回 False
        """
        return await self.get(id) is not None

    async def exists_by_field(self, field_name: str, value: Any) -> bool:
        """
        检查是否存在具有特定字段值的记录。

        Args:
            field_name: 要检查的字段名
            value: 要匹配的值

        Returns:
            如果存在返回 True，否则返回 False
        """
        statement = select(func.count()).select_from(self.model).where(
            getattr(self.model, field_name) == value
        )
        result = await self.session.execute(statement)
        count = result.scalar_one()
        return count > 0

    def _apply_filters(self, statement, filters: Dict[str, Any]):
        """
        将过滤器应用到 select 语句。

        支持:
        - 简单相等: {"name": "value"}
        - 操作符: {"name__like": "%value%", "age__gt": 18}
        - None/null 检查: {"deleted_at": None}
        - 列表成员: {"status__in": ["active", "pending"]}

        Args:
            statement: SQLAlchemy select 语句
            filters: 过滤器字典

        Returns:
            修改后的 select 语句
        """
        for key, value in filters.items():
            # 解析字段名和操作符
            if "__" in key:
                field_name, operator = key.split("__", 1)
            else:
                field_name = key
                operator = "eq"

            # 获取模型字段
            if not hasattr(self.model, field_name):
                logger.warning(f"Field {field_name} not found in {self.model.__name__}")
                continue

            field = getattr(self.model, field_name)

            # 应用操作符
            if operator == "eq":
                if value is None:
                    statement = statement.where(field.is_(None))
                else:
                    statement = statement.where(field == value)
            elif operator == "ne":
                if value is None:
                    statement = statement.where(field.is_not(None))
                else:
                    statement = statement.where(field != value)
            elif operator == "gt":
                statement = statement.where(field > value)
            elif operator == "gte":
                statement = statement.where(field >= value)
            elif operator == "lt":
                statement = statement.where(field < value)
            elif operator == "lte":
                statement = statement.where(field <= value)
            elif operator == "like":
                statement = statement.where(field.like(value))
            elif operator == "ilike":
                statement = statement.where(field.ilike(value))
            elif operator == "in":
                statement = statement.where(field.in_(value))
            elif operator == "not_in":
                statement = statement.where(~field.in_(value))
            else:
                logger.warning(f"Unknown operator: {operator}")

        return statement
