import time
from typing import AsyncGenerator

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase

from config import db_settings


class Base(DeclarativeBase):
    pass


# =============================================================================
# 메인 엔진 (FastAPI 요청용)
# =============================================================================
engine = create_async_engine(
    url=db_settings.MYSQL_URL,
    echo=False,
    pool_size=20,  # 기본 풀 크기: 20
    max_overflow=20,  # 추가 연결: 20 (총 최대 40)
    pool_timeout=30,  # 풀에서 연결 대기 시간 (초)
    pool_recycle=280,  # MySQL wait_timeout(기본 28800s, 클라우드는 보통 300s) 보다 짧게 설정
    pool_pre_ping=True,  # 연결 유효성 검사 (죽은 연결 자동 재연결)
    pool_reset_on_return="rollback",  # 반환 시 롤백으로 초기화
    connect_args={
        "connect_timeout": 10,  # DB 연결 타임아웃
        "charset": "utf8mb4",
    },
)

# 메인 세션 팩토리 (FastAPI DI용)
AsyncSessionLocal = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,
    autoflush=False,  # 명시적 flush 권장
)


# =============================================================================
# 백그라운드 태스크 전용 엔진 (메인 풀과 분리)
# =============================================================================
background_engine = create_async_engine(
    url=db_settings.MYSQL_URL,
    echo=False,
    pool_size=10,  # 백그라운드용 풀 크기: 10
    max_overflow=10,  # 추가 연결: 10 (총 최대 20)
    pool_timeout=60,  # 백그라운드는 대기 시간 여유있게
    pool_recycle=280,  # MySQL wait_timeout 보다 짧게 설정
    pool_pre_ping=True,  # 연결 유효성 검사 (죽은 연결 자동 재연결)
    pool_reset_on_return="rollback",
    connect_args={
        "connect_timeout": 10,
        "charset": "utf8mb4",
    },
)

# 백그라운드 세션 팩토리
BackgroundSessionLocal = async_sessionmaker(
    bind=background_engine,
    class_=AsyncSession,
    expire_on_commit=False,
    autoflush=False,
)


async def create_db_tables():
    import asyncio

    # 모델 import (테이블 메타데이터 등록용)
    from app.home.models import Image, Project  # noqa: F401
    from app.lyric.models import Lyric  # noqa: F401
    from app.song.models import Song  # noqa: F401
    from app.video.models import Video  # noqa: F401

    print("Creating database tables...")

    async with asyncio.timeout(10):
        async with engine.begin() as connection:
            await connection.run_sync(Base.metadata.create_all)


# FastAPI 의존성용 세션 제너레이터
async def get_session() -> AsyncGenerator[AsyncSession, None]:
    start_time = time.perf_counter()
    pool = engine.pool

    # 커넥션 풀 상태 로깅 (디버깅용)
    print(
        f"[get_session] ACQUIRE - pool_size: {pool.size()}, "
        f"in: {pool.checkedin()}, out: {pool.checkedout()}, "
        f"overflow: {pool.overflow()}"
    )

    async with AsyncSessionLocal() as session:
        acquire_time = time.perf_counter()
        print(
            f"[get_session] Session acquired in "
            f"{(acquire_time - start_time)*1000:.1f}ms"
        )
        try:
            yield session
        except Exception as e:
            await session.rollback()
            print(
                f"[get_session] ROLLBACK - error: {type(e).__name__}: {e}, "
                f"duration: {(time.perf_counter() - start_time)*1000:.1f}ms"
            )
            raise e
        finally:
            total_time = time.perf_counter() - start_time
            print(
                f"[get_session] RELEASE - duration: {total_time*1000:.1f}ms, "
                f"pool_out: {pool.checkedout()}"
            )


# 백그라운드 태스크용 세션 제너레이터
async def get_background_session() -> AsyncGenerator[AsyncSession, None]:
    start_time = time.perf_counter()
    pool = background_engine.pool

    print(
        f"[get_background_session] ACQUIRE - pool_size: {pool.size()}, "
        f"in: {pool.checkedin()}, out: {pool.checkedout()}, "
        f"overflow: {pool.overflow()}"
    )

    async with BackgroundSessionLocal() as session:
        acquire_time = time.perf_counter()
        print(
            f"[get_background_session] Session acquired in "
            f"{(acquire_time - start_time)*1000:.1f}ms"
        )
        try:
            yield session
        except Exception as e:
            await session.rollback()
            print(
                f"[get_background_session] ROLLBACK - "
                f"error: {type(e).__name__}: {e}, "
                f"duration: {(time.perf_counter() - start_time)*1000:.1f}ms"
            )
            raise e
        finally:
            total_time = time.perf_counter() - start_time
            print(
                f"[get_background_session] RELEASE - "
                f"duration: {total_time*1000:.1f}ms, "
                f"pool_out: {pool.checkedout()}"
            )


# 앱 종료 시 엔진 리소스 정리 함수
async def dispose_engine() -> None:
    print("[dispose_engine] Disposing database engines...")
    await engine.dispose()
    print("[dispose_engine] Main engine disposed")
    await background_engine.dispose()
    print("[dispose_engine] Background engine disposed - ALL DONE")
