"""Block until the configured database accepts connections.

Run by entrypoint.sh before the migrations:  python -m app.db_wait
SQLite needs no wait and returns at once. Exit code 1 when DB_CONNECT_TIMEOUT_SECONDS elapse.
"""
import logging
import sys
import time

from sqlalchemy import create_engine, text
from sqlalchemy.engine import make_url

from app.config import Config

log = logging.getLogger("app.db_wait")


def _now():
    return time.monotonic()


def wait_for_database(uri, timeout_seconds, interval_seconds=2.0, sleep=time.sleep):
    """Retry a connection to `uri` until it succeeds or `timeout_seconds` elapse; return True on success."""
    url = make_url(uri)
    if url.get_backend_name() == "sqlite":
        log.info("db.wait skipped engine=sqlite")
        return True
    engine = create_engine(uri, pool_pre_ping=True)
    deadline = _now() + timeout_seconds
    attempt = 0
    try:
        while True:
            attempt += 1
            try:
                with engine.connect() as conn:
                    conn.execute(text("SELECT 1"))
                log.info("db.wait ok host=%s attempts=%s", url.host, attempt)
                return True
            except Exception as exc:
                if _now() >= deadline:
                    log.error("db.wait timeout host=%s attempts=%s error=%s", url.host, attempt, type(exc).__name__)
                    return False
                log.info("db.wait retry host=%s attempt=%s", url.host, attempt)
                sleep(interval_seconds)
    finally:
        engine.dispose()


def main():
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s")
    ok = wait_for_database(Config.SQLALCHEMY_DATABASE_URI, Config.DB_CONNECT_TIMEOUT_SECONDS)
    sys.exit(0 if ok else 1)


if __name__ == "__main__":
    main()
