from collections.abc import Generator
from time import sleep

from sqlalchemy import MetaData, create_engine, text
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import Session, sessionmaker

from app.core import settings

engine = create_engine(
    settings.database_url,
    pool_pre_ping=True,
    connect_args={"connect_timeout": 3},
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


def create_schema(metadata: MetaData, attempts: int = 30, delay_seconds: int = 2) -> None:
    last_error: OperationalError | None = None
    for attempt in range(1, attempts + 1):
        try:
            print(f"Connecting to database, attempt {attempt}/{attempts}", flush=True)
            with engine.begin() as connection:
                connection.execute(text("SELECT 1"))
                metadata.create_all(bind=connection)
            print("Database schema is ready", flush=True)
            return
        except OperationalError as exc:
            last_error = exc
            print(f"Database is not ready: {exc}", flush=True)
            sleep(delay_seconds)
    if last_error:
        raise last_error


def get_db() -> Generator[Session]:
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
