"""订阅领域的请求级 command/query 依赖。"""

from typing import Any, cast

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

from app.adapters.external.server import MoviePilotServerHelper
from app.api.context import (
    get_async_session,
    get_host_runtime,
    get_subscription_history_repository,
    get_subscription_outbox,
    get_subscription_repository,
    get_subscription_transaction,
    get_sync_session,
)
from app.application.outbox import AsyncOutboxTransaction
from app.application.scheduling import start_scheduler_job
from app.application.servarr import ServarrSubscriptionService
from app.application.subscription.delete import (
    AsyncUnitOfWork as DeleteUnitOfWork,
    DeleteSubscribeCommand,
    SubscribeDeletionRepository,
)
from app.application.subscription.identity import DeleteSubscriptionsByIdentityCommand
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
from app.application.subscription.mutation import (
    AsyncUnitOfWork as MutationUnitOfWork,
    SubscriptionHistoryMutationRepository,
    SubscriptionMutationRepository,
    SubscriptionMutationService,
)
from app.application.subscription.query import SubscriptionQueryService
from app.application.subscription.search import SearchSubscriptionsCommand
from app.runtime.events import eventmanager
from app.runtime.log import logger
from app.schemas.types import EventType
from app.startup.composition.context import HostRuntime
from app.api.context import get_background_task_registry, resolve_background_task_registry
from app.runtime.tasks import TaskRegistry


async def _publish_subscribe_deleted(
    payload: dict[str, Any],
) -> None:
    """通过宿主事件总线发布已提交的订阅删除事件。"""
    await eventmanager.async_send_event(EventType.SubscribeDeleted, payload)


async def _publish_subscribe_modified(payload: dict[str, Any]) -> None:
    """通过宿主事件总线发布已提交的订阅修改事件。"""
    await eventmanager.async_send_event(EventType.SubscribeModified, payload)


def get_delete_subscribe_command(
    repository_port: object = Depends(get_subscription_repository),
    unit_of_work: object = Depends(get_subscription_transaction),
    outbox: AsyncOutboxTransaction = Depends(get_subscription_outbox),
) -> DeleteSubscribeCommand:
    """组装请求级订阅删除用例及其具体适配器。"""
    return DeleteSubscribeCommand(
        repository=cast(SubscribeDeletionRepository, repository_port),
        unit_of_work=cast(DeleteUnitOfWork, unit_of_work),
        publish_deleted=_publish_subscribe_deleted,
        report_deleted=MoviePilotServerHelper.async_sub_done_durable,
        outbox=outbox,
    )


def _log_subscribe_deleted_event_error(
    subscribe_id: int,
    error: Exception,
) -> None:
    """记录按媒体身份删除时的单条事件失败并允许后续事件继续。"""
    logger.error(
        f"发送订阅删除事件失败：{subscribe_id} - {error}",
        exc_info=True,
    )


def get_delete_subscriptions_by_identity_command(
    repository_port: object = Depends(get_subscription_repository),
    unit_of_work: object = Depends(get_subscription_transaction),
    outbox: AsyncOutboxTransaction = Depends(get_subscription_outbox),
) -> DeleteSubscriptionsByIdentityCommand:
    """组装请求级按媒体身份删除订阅用例。"""
    return DeleteSubscriptionsByIdentityCommand(
        repository=cast(SubscribeIdentityDeletionRepository, repository_port),
        unit_of_work=cast(DeleteUnitOfWork, unit_of_work),
        publish_deleted=_publish_subscribe_deleted,
        handle_event_error=_log_subscribe_deleted_event_error,
        outbox=outbox,
    )


def _start_subscription_search_batch(
    subscribe_ids: tuple[int, ...] | None,
    state: str | None,
) -> None:
    """把一个请求的搜索目标作为同一调度任务提交。"""
    if subscribe_ids is None:
        start_scheduler_job(
            "subscribe_search",
            sid=None,
            state=state,
            manual=True,
        )
        return
    start_scheduler_job(
        "subscribe_search",
        sids=subscribe_ids,
        state=None,
        manual=True,
    )


def get_search_subscriptions_command(
    task_registry: TaskRegistry = Depends(get_background_task_registry),
    db: AsyncSession = Depends(get_async_session),
    runtime: HostRuntime = Depends(get_host_runtime),
) -> SearchSubscriptionsCommand:
    """组装手工订阅搜索用例，并把调度延迟到响应后的后台任务。"""
    def schedule_search(
        subscribe_ids: tuple[int, ...] | None,
        state: str | None,
    ) -> None:
        """把当前用户的搜索目标提交为一个顺序后台批次。"""
        resolve_background_task_registry(task_registry).create_sync(
            _start_subscription_search_batch,
            subscribe_ids,
            state,
            owner="api.subscribe.search",
        )

    return SearchSubscriptionsCommand(
        repository=runtime.subscription.repository(db),
        schedule_search=schedule_search,
    )


def get_subscription_query_service(
    db: AsyncSession = Depends(get_async_session),
    runtime: HostRuntime = Depends(get_host_runtime),
) -> SubscriptionQueryService:
    """组装订阅和订阅历史异步查询服务。"""
    return SubscriptionQueryService(
        repository=runtime.subscription.repository(db),
        async_repository=runtime.subscription.repository(db),
        history_repository=runtime.subscription.history_repository(db),
    )


def get_subscription_mutation_service(
    repository_port: object = Depends(get_subscription_repository),
    history_repository: SubscriptionHistoryMutationRepository = Depends(
        get_subscription_history_repository
    ),
    unit_of_work: object = Depends(get_subscription_transaction),
    outbox: AsyncOutboxTransaction = Depends(get_subscription_outbox),
) -> SubscriptionMutationService:
    """组装异步订阅写服务。"""
    return SubscriptionMutationService(
        repository=cast(SubscriptionMutationRepository, repository_port),
        history_repository=history_repository,
        unit_of_work=cast(MutationUnitOfWork, unit_of_work),
        outbox=outbox,
        publish_modified=_publish_subscribe_modified,
    )


def get_subscription_sync_mutation_service(
    db: Session = Depends(get_sync_session),
    runtime: HostRuntime = Depends(get_host_runtime),
) -> SubscriptionMutationService:
    """组装同步订阅查询服务，供文件信息接口使用。"""
    return SubscriptionMutationService(
        repository=cast(
            SubscriptionMutationRepository,
            runtime.subscription.repository(db),
        )
    )


def get_servarr_subscription_service(
    async_db: AsyncSession = Depends(get_async_session),
    db: Session = Depends(get_sync_session),
    runtime: HostRuntime = Depends(get_host_runtime),
) -> ServarrSubscriptionService:
    """组装 Servarr 兼容路由的请求级订阅数据用例。"""
    return ServarrSubscriptionService(
        async_repository=runtime.subscription.repository(async_db),
        sync_repository=runtime.subscription.repository(db),
    )
