"""订阅写入端口的 SQLAlchemy 事务适配器。"""

from collections.abc import Callable
from contextlib import AbstractAsyncContextManager
from datetime import datetime, timezone

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session

from app.application.subscription.write import (
    AfterCommitEffect,
    AsyncAfterCommitEffect,
    AsyncCreateSubscriptionCommand,
    CreateSubscriptionCommand,
    subscription_added_event_key,
    subscription_added_notification_key,
    subscription_added_report_key,
)
from app.db.adapters.outbox import (
    SqlAlchemyAsyncOutboxStager,
    SqlAlchemyOutboxRepository,
)
from app.db.oper.subscribe import SubscribeOper
from app.db.uow import SqlAlchemyAsyncUnitOfWork, SqlAlchemyUnitOfWork


class TransactionalSubscribeWriter:
    """为每次订阅新增创建独占会话，并把提交权交给 Application Command。"""

    def __init__(
        self,
        sync_session: Callable[[], Session],
        async_session: Callable[
            [],
            AbstractAsyncContextManager[AsyncSession],
        ],
    ) -> None:
        """注入同步会话工厂和异步会话作用域。"""
        self._sync_session = sync_session
        self._async_session = async_session

    def add(
        self,
        identity: dict,
        payload: dict,
        username: str | None = None,
        after_commit: AfterCommitEffect | None = None,
        notification: dict[str, object] | None = None,
    ) -> tuple[int, str]:
        """在独占同步会话内执行一次完整订阅新增事务。"""
        session = self._sync_session()
        try:
            outbox = SqlAlchemyOutboxRepository(session)
            command = CreateSubscriptionCommand(
                repository=SubscribeOper(session),
                unit_of_work=SqlAlchemyUnitOfWork(session),
                outbox=outbox,
            )

            def delivered(subscribe_id: int) -> None:
                """执行旧 post-commit 编排，全部成功后收口 durable intent。"""
                if after_commit:
                    after_commit(subscribe_id)
                    outbox.complete_by_event_key(
                        subscription_added_event_key(subscribe_id, payload),
                        datetime.now(timezone.utc),
                    )
                    if notification:
                        outbox.complete_by_event_key(
                            subscription_added_notification_key(subscribe_id, payload),
                            datetime.now(timezone.utc),
                        )
                    outbox.complete_by_event_key(
                        subscription_added_report_key(subscribe_id, payload),
                        datetime.now(timezone.utc),
                    )

            return command.execute(
                identity,
                payload,
                username,
                delivered,
                notification,
            )
        finally:
            session.close()

    async def async_add(
        self,
        identity: dict,
        payload: dict,
        username: str | None = None,
        after_commit: AsyncAfterCommitEffect | None = None,
        notification: dict[str, object] | None = None,
    ) -> tuple[int, str]:
        """在独占异步会话作用域内执行一次完整订阅新增事务。"""
        async with self._async_session() as session:
            outbox = SqlAlchemyAsyncOutboxStager(session)
            command = AsyncCreateSubscriptionCommand(
                repository=SubscribeOper(session),
                unit_of_work=SqlAlchemyAsyncUnitOfWork(session),
                outbox=outbox,
            )

            async def delivered(subscribe_id: int) -> None:
                """异步执行旧编排，全部成功后收口 durable intent。"""
                if after_commit:
                    await after_commit(subscribe_id)
                    await outbox.complete_by_event_key(
                        subscription_added_event_key(subscribe_id, payload),
                        datetime.now(timezone.utc),
                    )
                    if notification:
                        await outbox.complete_by_event_key(
                            subscription_added_notification_key(subscribe_id, payload),
                            datetime.now(timezone.utc),
                        )
                    await outbox.complete_by_event_key(
                        subscription_added_report_key(subscribe_id, payload),
                        datetime.now(timezone.utc),
                    )

            return await command.execute(
                identity,
                payload,
                username,
                delivered,
                notification,
            )
