"""
异步数据库配置模块

提供 SQLAlchemy 2.0 AsyncSession 支持，用于完全异步的数据库操作。
支持 PostgreSQL (asyncpg) 和 SQLite (aiosqlite)。
"""

import logging
import os
import time
from typing import AsyncGenerator

from sqlalchemy import text
from sqlalchemy.ext.asyncio import (
    create_async_engine,
    AsyncSession,
    async_sessionmaker,
    AsyncEngine,
)
from sqlalchemy.pool import NullPool
from sqlmodel import SQLModel

from src.config.settings import DB_PATH, DATABASE_URL

logger = logging.getLogger(__name__)

# Slow query threshold in seconds
SLOW_QUERY_THRESHOLD = float(os.getenv("SLOW_QUERY_THRESHOLD", "1.0"))

# Connection pool configuration from environment variables
POOL_SIZE = int(os.getenv("DB_POOL_SIZE", "20"))
MAX_OVERFLOW = int(os.getenv("DB_MAX_OVERFLOW", "10"))
POOL_TIMEOUT = int(os.getenv("DB_POOL_TIMEOUT", "30"))
POOL_RECYCLE = int(os.getenv("DB_POOL_RECYCLE", "3600"))
POOL_PRE_PING = os.getenv("DB_POOL_PRE_PING", "true").lower() == "true"

# Global engine instance
async_engine: AsyncEngine | None = None
async_session_maker: async_sessionmaker[AsyncSession] | None = None


def _get_async_url() -> str:
    """Generate async database URL from settings."""
    if DATABASE_URL:
        # Convert PostgreSQL URL to async version
        if DATABASE_URL.startswith("postgresql://"):
            return DATABASE_URL.replace("postgresql://", "postgresql+asyncpg://", 1)
        elif DATABASE_URL.startswith("postgresql+psycopg2://"):
            return DATABASE_URL.replace("postgresql+psycopg2://", "postgresql+asyncpg://", 1)
        return DATABASE_URL
    else:
        # SQLite async URL
        return f"sqlite+aiosqlite:///{DB_PATH}"


def init_async_engine() -> AsyncEngine:
    """Initialize async database engine with optimized connection pooling."""
    global async_engine

    if async_engine is not None:
        return async_engine

    async_url = _get_async_url()

    if "postgresql" in async_url:
        # PostgreSQL async configuration
        async_engine = create_async_engine(
            async_url,
            echo=False,
            pool_size=POOL_SIZE,
            max_overflow=MAX_OVERFLOW,
            pool_timeout=POOL_TIMEOUT,
            pool_recycle=POOL_RECYCLE,
            pool_pre_ping=POOL_PRE_PING,
            connect_args={
                "timeout": 10,
                "command_timeout": 30,
            },
        )
        logger.info(
            f"Async PostgreSQL engine created with pool_size={POOL_SIZE}, "
            f"max_overflow={MAX_OVERFLOW}, pool_timeout={POOL_TIMEOUT}s"
        )
    else:
        # SQLite async configuration
        async_engine = create_async_engine(
            async_url,
            echo=False,
            poolclass=NullPool,  # SQLite doesn't support connection pooling well
            connect_args={
                "timeout": 30,
                "check_same_thread": False,
            },
        )
        logger.info(f"Async SQLite engine created at {DB_PATH}")

    return async_engine


def init_async_session_maker() -> async_sessionmaker[AsyncSession]:
    """Initialize async session maker."""
    global async_session_maker

    if async_session_maker is not None:
        return async_session_maker

    engine = init_async_engine()

    async_session_maker = async_sessionmaker(
        engine,
        class_=AsyncSession,
        expire_on_commit=False,
        autocommit=False,
        autoflush=False,
    )

    return async_session_maker


async def init_db_async():
    """Initialize database tables asynchronously."""
    engine = init_async_engine()

    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)

    logger.info("Database tables initialized (async)")


async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    """
    Dependency for getting async database session.

    Usage:
        @router.get("/items")
        async def get_items(session: AsyncSession = Depends(get_async_session)):
            ...
    """
    session_maker = init_async_session_maker()

    async with session_maker() as session:
        try:
            yield session
        finally:
            await session.close()


async def get_async_session_context() -> AsyncSession:
    """
    Get async session as context manager.

    Usage:
        async with get_async_session_context() as session:
            result = await session.execute(...)
    """
    session_maker = init_async_session_maker()
    return session_maker()


async def get_pool_status_async() -> dict:
    """
    Get current connection pool status for monitoring.
    Returns pool statistics including size, checked out connections, etc.
    """
    engine = init_async_engine()
    pool = engine.pool

    status = {
        "pool_size": getattr(pool, "size", lambda: 0)(),
        "checked_out": getattr(pool, "checkedout", lambda: 0)(),
        "overflow": getattr(pool, "overflow", lambda: 0)(),
    }

    return status


async def check_database_health_async() -> tuple[bool, str]:
    """
    Check database connectivity and health asynchronously.
    Returns (is_healthy, message).
    """
    try:
        engine = init_async_engine()
        async with engine.connect() as conn:
            start_time = time.time()
            await conn.execute(text("SELECT 1"))
            elapsed = time.time() - start_time

        return True, f"Database connection healthy ({elapsed:.3f}s)"
    except Exception as e:
        logger.error(f"Database health check failed: {e}")
        return False, f"Database connection failed: {str(e)}"


async def close_async_engine():
    """Close async engine and cleanup connections."""
    global async_engine, async_session_maker

    if async_engine is not None:
        await async_engine.dispose()
        async_engine = None
        async_session_maker = None
        logger.info("Async database engine closed")


# Import guards for sync operations
async def migrate_sync_to_async():
    """
    Migration helper to ensure both sync and async engines are initialized.
    During transition period, both can coexist.
    """
    # Initialize async engine
    init_async_engine()
    init_async_session_maker()

    # Sync engine is already initialized in database.py
    logger.info("Both sync and async database engines are ready")
