"""Controller session decorators.

`with_session` is an HTTP controller helper: it opens one SQLAlchemy session
for a Resource handler and injects it as the first argument after `self`.
Write handlers commit on success and roll back on failure. They use a regular
Session context so existing services may commit an intermediate unit and keep
using the same Session through SQLAlchemy's autobegin behavior. Pure read
handlers may opt out with `write=False`.
"""

from collections.abc import Callable
from functools import wraps
from typing import Concatenate, overload

from sqlalchemy.orm import Session

from core.db.session_factory import session_factory


@overload
def with_session[T, **P, R](
    view: Callable[Concatenate[T, Session, P], R],
    *,
    write: bool = True,
) -> Callable[Concatenate[T, P], R]: ...


@overload
def with_session[T, **P, R](
    view: None = None,
    *,
    write: bool = True,
) -> Callable[[Callable[Concatenate[T, Session, P], R]], Callable[Concatenate[T, P], R]]: ...


def with_session[T, **P, R](
    view: Callable[Concatenate[T, Session, P], R] | None = None,
    *,
    write: bool = True,
) -> (
    Callable[Concatenate[T, P], R] | Callable[[Callable[Concatenate[T, Session, P], R]], Callable[Concatenate[T, P], R]]
):
    """Inject a request-scoped session and finalize write handlers."""

    def decorator(view: Callable[Concatenate[T, Session, P], R]) -> Callable[Concatenate[T, P], R]:
        @wraps(view)
        def wrapper(self: T, *args: P.args, **kwargs: P.kwargs) -> R:
            if write:
                with session_factory.create_session() as session:
                    try:
                        result = view(self, session, *args, **kwargs)
                        session.commit()
                        return result
                    except Exception:
                        session.rollback()  # guard-ignore: no-new-controller-sqlalchemy -- decorator owns rollback
                        raise

            with session_factory.create_session() as session:
                return view(self, session, *args, **kwargs)

        return wrapper

    if view is None:
        return decorator
    return decorator(view)
