import os
import sys
from pathlib import Path
from sqlmodel import SQLModel, create_engine, Session
from typing import Annotated, AsyncGenerator
from fastapi import Depends
from contextlib import asynccontextmanager
from sqlalchemy import event
from sqlalchemy.ext.asyncio import (
    AsyncSession,
    async_sessionmaker,
    create_async_engine,
)

# In frozen PyInstaller builds, store the database in %APPDATA%\CerebionRivet\
# (writable by the user). In development, store it next to the app package.
if getattr(sys, "frozen", False):
    if sys.platform == "win32":
        _appdata = os.environ.get("APPDATA") or str(Path.home())
        _data_dir = Path(_appdata) / "CerebionRivet"
    elif sys.platform == "darwin":
        _data_dir = Path.home() / "Library" / "Application Support" / "CerebionRivet"
    else:
        _data_dir = Path(os.environ.get("XDG_DATA_HOME") or Path.home() / ".local" / "share") / "CerebionRivet"
else:
    _data_dir = Path(__file__).parent.parent  # rivet_server/
_data_dir.mkdir(parents=True, exist_ok=True)

sqlite_file_name = str(_data_dir / "database.db")

# Synchronous engine (used by most app routes)
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)

# Asynchronous engine (used by fastapi-users auth flows)
async_sqlite_url = f"sqlite+aiosqlite:///{sqlite_file_name}"
async_engine = create_async_engine(async_sqlite_url, echo=False, future=True)


def _set_sqlite_pragmas(dbapi_conn, connection_record):
    """Optimize SQLite performance with PRAGMAs applied once per connection."""
    cursor = dbapi_conn.cursor()
    cursor.execute("PRAGMA journal_mode = WAL")       # Write-Ahead Logging for better concurrent reads
    cursor.execute("PRAGMA synchronous = NORMAL")     # Safe with WAL, avoids fsync on every write
    cursor.execute("PRAGMA cache_size = -64000")      # 64MB page cache (negative = KB)
    cursor.execute("PRAGMA temp_store = MEMORY")      # Keep temp tables in memory
    cursor.execute("PRAGMA mmap_size = 268435456")    # 256MB memory-mapped I/O
    cursor.close()


event.listen(engine, "connect", _set_sqlite_pragmas)
event.listen(async_engine.sync_engine, "connect", _set_sqlite_pragmas)
async_session_maker = async_sessionmaker(
    async_engine, expire_on_commit=False, class_=AsyncSession
)


def create_db_and_tables():
    """Create database tables"""
    SQLModel.metadata.create_all(engine)


def get_session():
    """Get database session for dependency injection"""
    with Session(engine) as session:
        yield session


# Type annotation for database session dependency
SessionDep = Annotated[Session, Depends(get_session)]


# Async session dependency for components that require AsyncSession (e.g., fastapi-users)
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        yield session

AsyncSessionDep = Annotated[AsyncSession, Depends(get_async_session)]


@asynccontextmanager
async def get_session_context():
    """Get database session for background tasks"""
    with Session(engine) as session:
        yield session
