from __future__ import annotations
import os
from contextlib import asynccontextmanager
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from quart import Quart, g

DATABASE_URL = (
    os.getenv("DATABASE_URL_ASYNC")
    or os.getenv("DATABASE_URL")
    or "postgresql+asyncpg://localhost/coop"
)

_engine = create_async_engine(
    DATABASE_URL,
    future=True,
    echo=False,
    pool_pre_ping=True,
    pool_size=-1  # ned to look at this!!!
)

_Session = async_sessionmaker(
    bind=_engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

@asynccontextmanager
async def get_session():
    """Always create a fresh AsyncSession for this block."""
    sess = _Session()
    try:
        yield sess
    finally:
        await sess.close()



def register_db(app: Quart):

  @app.before_request
  async def open_session():
      g.s = _Session()
      g.tx = await g.s.begin()
      g.had_error = False

  @app.after_request
  async def maybe_commit(response):
      # Runs BEFORE bytes are sent.
      if not g.had_error and 200 <= response.status_code < 400:
          try:
              if hasattr(g, "tx"):
                  await g.tx.commit()
          except Exception as e:
              print(f'commit failed {e}')
              if hasattr(g, "tx"):
                  await g.tx.rollback()
              from quart import make_response
              return await make_response("Commit failed", 500)
      return response

  @app.teardown_request
  async def finish(exc):
      try:
          # If an exception occurred OR we didn't commit (still in txn), roll back.
          if hasattr(g, "s"):
              if exc is not None or g.s.in_transaction():
                  if hasattr(g, "tx"):
                      await g.tx.rollback()
      finally:
          if hasattr(g, "s"):
              await g.s.close()

  @app.errorhandler(Exception)
  async def mark_error(e):
      g.had_error = True
      raise
