"""Chain durable 事件写入端口的 SQLAlchemy 适配器。"""

from __future__ import annotations

from collections.abc import Callable
from typing import Any

from sqlalchemy.orm import Session

from app.application.chain.durable_events import (
    ChainDurableEventWriter,
    TransferHistoryRef,
    download_added_event_key,
    snapshot_download_added,
    snapshot_transfer_result,
    transfer_result_event_key,
)
from app.application.history import TransferHistoryRecord, TransferHistoryWriter
from app.application.outbox import DurableEventCommand, OutboxIntent
from app.db.adapters.outbox import SqlAlchemyOutboxRepository
from app.db.oper.downloadhistory import DownloadHistoryOper
from app.db.oper.transferhistory import TransferHistoryOper
from app.db.uow import SqlAlchemyUnitOfWork


class _StagingTransferHistoryWriter:
    """让既有历史字段映射复用无提交的 replace 适配器。"""

    def __init__(self, repository: TransferHistoryOper) -> None:
        """保存绑定调用方 Session 的整理历史仓储。"""
        self._repository = repository

    def get_by_src(
        self,
        src: str,
        storage: str | None = None,
    ) -> TransferHistoryRecord | None:
        """转发按源路径读取。"""
        return self._repository.get_by_src(src, storage)

    def get_success_by_src(
        self,
        src: str,
        storage: str | None = None,
    ) -> TransferHistoryRecord | None:
        """转发按源路径读取成功记录。"""
        return self._repository.get_success_by_src(src, storage)

    def add_force(self, **payload: Any) -> TransferHistoryRecord:
        """保持应用层旧端口名，但只暂存替换而不自行提交。"""
        return self._repository.stage_replace_by_src(**payload)


class TransactionalChainDurableEventWriter(ChainDurableEventWriter):
    """为每次 Chain 结果事件创建独占同步 Session 和 UoW。"""

    def __init__(self, session_factory: Callable[[], Session]) -> None:
        """注入惰性同步 Session 工厂。"""
        self._session_factory = session_factory

    def download_added(
        self,
        *,
        history_payload: dict[str, Any],
        file_payloads: list[dict[str, Any]],
        event_payload: dict[str, Any],
        after_commit: Callable[[], None],
        publish: Callable[[dict[str, Any]], None],
    ) -> None:
        """原子写下载历史、文件清单和 DownloadAdded intent。"""
        session = self._session_factory()
        try:
            repository = DownloadHistoryOper(session)
            outbox = SqlAlchemyOutboxRepository(session)
            command = DurableEventCommand(
                unit_of_work=SqlAlchemyUnitOfWork(session),
                outbox=outbox,
            )
            event_key = download_added_event_key(event_payload)
            event_payload["idempotency_key"] = event_key

            def stage_business() -> None:
                """在同一事务暂存下载历史和可选文件清单。"""
                repository.stage_add(history_payload)
                if file_payloads:
                    repository.stage_add_files(file_payloads)

            command.execute(
                intent=OutboxIntent(
                    event_key=event_key,
                    topic="download.added",
                    payload=snapshot_download_added(event_payload),
                ),
                stage_business=stage_business,
                after_commit=after_commit,
                publish=lambda: publish(event_payload),
            )
        finally:
            session.close()

    def transfer_result(
        self,
        *,
        topic: str,
        stage_history: Callable[[TransferHistoryWriter], TransferHistoryRecord | None],
        event_payload: dict[str, Any],
        publish: Callable[[dict[str, Any]], None],
    ) -> TransferHistoryRecord | None:
        """原子写整理历史与结果 intent，并返回脱离 Session 的最小投影。"""
        session = self._session_factory()
        try:
            staging = _StagingTransferHistoryWriter(TransferHistoryOper(session))
            command = DurableEventCommand(
                unit_of_work=SqlAlchemyUnitOfWork(session),
                outbox=SqlAlchemyOutboxRepository(session),
            )

            def stage_business() -> TransferHistoryRef | None:
                """复用历史字段映射，并在 flush 后冻结安全投影。"""
                history = stage_history(staging)
                if history is None:
                    return None
                return TransferHistoryRef(
                    id=history.id,
                    status=bool(history.status),
                    src=history.src,
                    src_storage=history.src_storage,
                    src_fileitem=history.src_fileitem,
                )

            def build_intent(
                history: TransferHistoryRef | None,
            ) -> OutboxIntent:
                """历史 ID 确定后构造事件键与可恢复快照。"""
                if history is None:
                    raise RuntimeError("整理历史暂存失败，无法登记 durable 结果事件")
                event_key = transfer_result_event_key(topic, history.id)
                event_payload["transfer_history_id"] = history.id
                event_payload["idempotency_key"] = event_key
                return OutboxIntent(
                    event_key=event_key,
                    topic=topic,
                    payload=snapshot_transfer_result(event_payload),
                )

            return command.execute(
                intent=build_intent,
                stage_business=stage_business,
                publish=lambda: publish(event_payload),
            )
        finally:
            session.close()
