from contextlib import contextmanager
import os

from flask import current_app, has_app_context
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker


_engine = None
_engine_url = None
_Session = None


def get_database_url() -> str:
    if has_app_context():
        return str(current_app.config["DATABASE_URL"])
    return os.getenv("DATABASE_URL", "sqlite:///l4d2web.db")


def get_engine():
    global _engine
    global _engine_url
    global _Session

    db_url = get_database_url()
    if _engine is None or _engine_url != db_url:
        connect_args = {"check_same_thread": False} if db_url.startswith("sqlite") else {}
        _engine = create_engine(db_url, connect_args=connect_args)
        if db_url.startswith("sqlite"):
            with _engine.connect() as conn:
                conn.exec_driver_sql("PRAGMA journal_mode=WAL;")
                conn.exec_driver_sql("PRAGMA busy_timeout=5000;")
        _engine_url = db_url
        _Session = sessionmaker(bind=_engine, expire_on_commit=False)
    return _engine


def init_db() -> None:
    from l4d2web.models import Base

    Base.metadata.create_all(bind=get_engine())


@contextmanager
def session_scope() -> Session:
    global _Session
    if _Session is None:
        get_engine()
    assert _Session is not None
    session = _Session()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()
