package cn.nianxx.thhotel.workflows.reservation.repository;

import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiBatchDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiBatchSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiQueryOrderSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiQueryTaskSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationAttemptDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationAttemptSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOrderDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOrderSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationPageSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskCardDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskCardSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskDraft;
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskSnapshot;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationOrderListQueryRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationTaskWorkbenchQueryRequest;
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOrderKeyType;
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOrderStatus;
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOrderVisibility;
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationTaskStatus;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationAiBatchEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationAiTransitionEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationAuditLogEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationOperaOperationAttemptEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationOperaOperationEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationOrderEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationTaskCardEntity;
import cn.nianxx.thhotel.workflows.reservation.domain.ReservationTaskEntity;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationAiBatchMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationAiTransitionMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationAuditLogMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationOperaOperationAttemptMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationOperaOperationMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationOrderMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationTaskCardMapper;
import cn.nianxx.thhotel.workflows.reservation.mapper.ReservationTaskMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.stereotype.Repository;

/**
 * Reservation AI 工作流 MyBatis-Plus 持久化实现。数据库 Entity 和 Mapper 不泄漏到 Service。
 */
@Repository
public class MybatisReservationAiWorkflowRepository implements ReservationAiWorkflowRepository {

    private final ReservationAiBatchMapper batchMapper;
    private final ReservationAiTransitionMapper transitionMapper;
    private final ReservationOrderMapper orderMapper;
    private final ReservationTaskMapper taskMapper;
    private final ReservationTaskCardMapper taskCardMapper;
    private final ReservationOperaOperationMapper operaOperationMapper;
    private final ReservationOperaOperationAttemptMapper operaOperationAttemptMapper;
    private final ReservationAuditLogMapper auditLogMapper;

    /**
     * 注入本工作流 Mapper，Repository 负责实体转换和查询细节。
     */
    public MybatisReservationAiWorkflowRepository(
            ReservationAiBatchMapper batchMapper,
            ReservationAiTransitionMapper transitionMapper,
            ReservationOrderMapper orderMapper,
            ReservationTaskMapper taskMapper,
            ReservationTaskCardMapper taskCardMapper,
            ReservationOperaOperationMapper operaOperationMapper,
            ReservationOperaOperationAttemptMapper operaOperationAttemptMapper,
            ReservationAuditLogMapper auditLogMapper) {
        this.batchMapper = batchMapper;
        this.transitionMapper = transitionMapper;
        this.orderMapper = orderMapper;
        this.taskMapper = taskMapper;
        this.taskCardMapper = taskCardMapper;
        this.operaOperationMapper = operaOperationMapper;
        this.operaOperationAttemptMapper = operaOperationAttemptMapper;
        this.auditLogMapper = auditLogMapper;
    }

    /**
     * 按 SourceMessage 读取第一条批次。第一版同一 SourceMessage 不支持不同 payload 重抽版本。
     */
    @Override
    public Optional<ReservationAiBatchSnapshot> findBatchBySourceMessageId(String hotelId, Long sourceMessageId) {
        ReservationAiBatchEntity entity = batchMapper.selectOne(Wrappers.<ReservationAiBatchEntity>lambdaQuery()
                .eq(ReservationAiBatchEntity::getHotelId, hotelId)
                .eq(ReservationAiBatchEntity::getSourceMessageId, sourceMessageId)
                .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toBatchSnapshot);
    }

    /**
     * 插入 AI 接收批次。
     */
    @Override
    public Long insertBatch(ReservationAiBatchDraft draft) {
        ReservationAiBatchEntity entity = new ReservationAiBatchEntity();
        entity.setHotelId(draft.hotelId());
        entity.setSourceMessageId(draft.sourceMessageId());
        entity.setRequestPayloadSha256(draft.requestPayloadSha256());
        entity.setBatchIdempotencyKey(draft.batchIdempotencyKey());
        entity.setClientId(draft.clientId());
        entity.setRequestId(draft.requestId());
        entity.setReceivedAt(draft.receivedAt());
        entity.setItemCount(draft.itemCount());
        entity.setExtractionWarningsJson(draft.extractionWarningsJson());
        entity.setCreatedAt(draft.receivedAt());
        entity.setUpdatedAt(draft.receivedAt());
        batchMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 插入单条 AI 过渡记录。
     */
    @Override
    public Long insertTransition(ReservationAiTransitionDraft draft) {
        ReservationAiTransitionEntity entity = new ReservationAiTransitionEntity();
        entity.setHotelId(draft.hotelId());
        entity.setBatchId(draft.batchId());
        entity.setSourceMessageId(draft.sourceMessageId());
        entity.setSourceEventIndex(draft.sourceEventIndex());
        entity.setArrayIndex(draft.arrayIndex());
        entity.setExecutionOrder(draft.executionOrder());
        entity.setCatalogCode(draft.catalogCode());
        entity.setSkillId(draft.skillId());
        entity.setResultType(draft.resultType());
        entity.setAiTaskType(draft.aiTaskType());
        entity.setRouteCode(draft.routeCode());
        entity.setSystemProcessCategory(draft.systemProcessCategory());
        entity.setSystemTaskType(draft.systemTaskType());
        entity.setTaskCardType(draft.taskCardType());
        entity.setTaskSubtype(draft.taskSubtype());
        entity.setCurrentOrHistory(draft.currentOrHistory());
        entity.setGroupCode(draft.groupCode());
        entity.setConfirmationNumber(draft.confirmationNumber());
        entity.setItemPayloadSha256(draft.itemPayloadSha256());
        entity.setItemIdempotencyKey(draft.itemIdempotencyKey());
        entity.setManualReasonCode(draft.manualReasonCode());
        entity.setParentSourceEventIndex(draft.parentSourceEventIndex());
        entity.setLinkedTaskGroupId(draft.linkedTaskGroupId());
        entity.setBlockedUntilParentCompleted(Boolean.TRUE.equals(draft.blockedUntilParentCompleted()));
        entity.setAiPayloadJson(draft.aiPayloadJson());
        entity.setCaseKeysJson(draft.caseKeysJson());
        entity.setExtractedFieldsJson(draft.extractedFieldsJson());
        entity.setManualReviewJson(draft.manualReviewJson());
        entity.setInformationalMessageJson(draft.informationalMessageJson());
        entity.setAttachmentsJson(draft.attachmentsJson());
        entity.setContextUsedJson(draft.contextUsedJson());
        entity.setAdapterErrorCode(draft.adapterErrorCode());
        entity.setAdapterErrorMessage(draft.adapterErrorMessage());
        entity.setCreatedAt(draft.now());
        entity.setUpdatedAt(draft.now());
        transitionMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 根据业务号查找 ACTIVE 订单，LOGIC_DELETED 和 ENDED 不再自动承载新任务。
     */
    @Override
    public Optional<ReservationOrderSnapshot> findActiveOrderByBusinessKey(
            String hotelId,
            String orderKeyType,
            String orderBusinessKey) {
        if (orderBusinessKey == null || orderBusinessKey.isBlank()) {
            return Optional.empty();
        }
        ReservationOrderEntity entity = orderMapper.selectOne(Wrappers.<ReservationOrderEntity>lambdaQuery()
                .eq(ReservationOrderEntity::getHotelId, hotelId)
                .eq(ReservationOrderEntity::getOrderKeyType, orderKeyType)
                .eq(ReservationOrderEntity::getOrderBusinessKey, orderBusinessKey)
                .eq(ReservationOrderEntity::getOrderStatus, ReservationOrderStatus.ACTIVE.name())
                .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toOrderSnapshot);
    }

    /**
     * 插入订单并返回快照。
     */
    @Override
    public ReservationOrderSnapshot insertOrder(ReservationOrderDraft draft) {
        ReservationOrderEntity entity = new ReservationOrderEntity();
        entity.setHotelId(draft.hotelId());
        entity.setOrderKeyType(draft.orderKeyType());
        entity.setOrderBusinessKey(draft.orderBusinessKey());
        entity.setActiveBusinessKey(draft.activeBusinessKey());
        entity.setTemporaryOrderCode(draft.temporaryOrderCode());
        entity.setOrderStatus(draft.orderStatus());
        entity.setOrderVisibility(draft.orderVisibility());
        entity.setBusinessKeySource(draft.businessKeySource());
        entity.setBusinessKeyBackfilledAt(draft.businessKeyBackfilledAt());
        entity.setDisplayName(draft.displayName());
        entity.setSourceMessageId(draft.sourceMessageId());
        entity.setVersion(0L);
        entity.setCreatedAt(draft.now());
        entity.setUpdatedAt(draft.now());
        orderMapper.insert(entity);
        return toOrderSnapshot(entity);
    }

    /**
     * 计算下一条参与队列任务的顺序。Message Notification 不参与队列，但仍保存当前返回值。
     */
    @Override
    public int nextExecutionOrder(String hotelId, Long orderId) {
        ReservationTaskEntity lastTask = taskMapper.selectOne(Wrappers.<ReservationTaskEntity>lambdaQuery()
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .eq(ReservationTaskEntity::getOrderId, orderId)
                .orderByDesc(ReservationTaskEntity::getExecutionOrder)
                .last("LIMIT 1"));
        return lastTask == null || lastTask.getExecutionOrder() == null ? 1 : lastTask.getExecutionOrder() + 1;
    }

    /**
     * 插入系统任务。
     */
    @Override
    public Long insertTask(ReservationTaskDraft draft) {
        ReservationTaskEntity entity = new ReservationTaskEntity();
        entity.setHotelId(draft.hotelId());
        entity.setOrderId(draft.orderId());
        entity.setSourceMessageId(draft.sourceMessageId());
        entity.setAiTransitionId(draft.aiTransitionId());
        entity.setResultType(draft.resultType());
        entity.setAiTaskType(draft.aiTaskType());
        entity.setSystemTaskType(draft.systemTaskType());
        entity.setTaskCardType(draft.taskCardType());
        entity.setTaskSubtype(draft.taskSubtype());
        entity.setTaskStatus(draft.taskStatus());
        entity.setQueueParticipation(Boolean.TRUE.equals(draft.queueParticipation()));
        entity.setExecutionOrder(draft.executionOrder());
        entity.setParentSourceEventIndex(draft.parentSourceEventIndex());
        entity.setLinkedTaskGroupId(draft.linkedTaskGroupId());
        entity.setBlockedUntilParentCompleted(Boolean.TRUE.equals(draft.blockedUntilParentCompleted()));
        entity.setCompletedAt(draft.completedAt());
        entity.setVersion(0L);
        entity.setCreatedAt(draft.now());
        entity.setUpdatedAt(draft.now());
        taskMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 插入任务卡初始记录。
     */
    @Override
    public Long insertTaskCard(ReservationTaskCardDraft draft) {
        ReservationTaskCardEntity entity = new ReservationTaskCardEntity();
        entity.setHotelId(draft.hotelId());
        entity.setTaskId(draft.taskId());
        entity.setTaskCardType(draft.taskCardType());
        entity.setFieldContractVersion(draft.fieldContractVersion());
        entity.setAiPayloadJson(draft.aiPayloadJson());
        entity.setReviewStatus(draft.reviewStatus());
        entity.setCreatedAt(draft.now());
        entity.setUpdatedAt(draft.now());
        taskCardMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 按任务 ID 查询任务快照，用于详情和人工复核转换。
     */
    @Override
    public Optional<ReservationTaskSnapshot> findTaskById(Long taskId) {
        return Optional.ofNullable(taskMapper.selectById(taskId)).map(this::toTaskSnapshot);
    }

    /**
     * 按任务 ID 查询任务卡快照。
     */
    @Override
    public Optional<ReservationTaskCardSnapshot> findTaskCardByTaskId(String hotelId, Long taskId) {
        ReservationTaskCardEntity entity = taskCardMapper.selectOne(Wrappers.<ReservationTaskCardEntity>lambdaQuery()
                .eq(ReservationTaskCardEntity::getHotelId, hotelId)
                .eq(ReservationTaskCardEntity::getTaskId, taskId)
                .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toTaskCardSnapshot);
    }

    /**
     * 按 AI transition ID 查询展示快照，用于任务详情补充 V3 入站路由上下文。
     */
    @Override
    public Optional<ReservationAiTransitionSnapshot> findTransitionById(String hotelId, Long transitionId) {
        if (transitionId == null) {
            return Optional.empty();
        }
        ReservationAiTransitionEntity entity = transitionMapper.selectOne(
                Wrappers.<ReservationAiTransitionEntity>lambdaQuery()
                        .eq(ReservationAiTransitionEntity::getHotelId, hotelId)
                        .eq(ReservationAiTransitionEntity::getId, transitionId)
                        .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toAiTransitionSnapshot);
    }

    /**
     * 按 AI 批次查询全部 transition 展示快照，按 AI 返回顺序稳定排序。
     */
    @Override
    public List<ReservationAiTransitionSnapshot> findTransitionsByBatchId(String hotelId, Long batchId) {
        if (batchId == null) {
            return List.of();
        }
        return transitionMapper.selectList(Wrappers.<ReservationAiTransitionEntity>lambdaQuery()
                        .eq(ReservationAiTransitionEntity::getHotelId, hotelId)
                        .eq(ReservationAiTransitionEntity::getBatchId, batchId)
                        .orderByAsc(ReservationAiTransitionEntity::getArrayIndex)
                        .orderByAsc(ReservationAiTransitionEntity::getId))
                .stream()
                .map(this::toAiTransitionSnapshot)
                .toList();
    }

    /**
     * 查询同订单前置队列任务，Message Notification 等非队列任务不会进入结果。
     */
    @Override
    public List<ReservationTaskSnapshot> findQueueTasksBefore(String hotelId, Long orderId, Integer executionOrder) {
        return taskMapper.selectList(Wrappers.<ReservationTaskEntity>lambdaQuery()
                        .eq(ReservationTaskEntity::getHotelId, hotelId)
                        .eq(ReservationTaskEntity::getOrderId, orderId)
                        .eq(ReservationTaskEntity::getQueueParticipation, Boolean.TRUE)
                        .lt(ReservationTaskEntity::getExecutionOrder, executionOrder)
                        .orderByAsc(ReservationTaskEntity::getExecutionOrder))
                .stream()
                .map(this::toTaskSnapshot)
                .toList();
    }

    /**
     * 按订单 ID 查询订单快照。
     */
    @Override
    public Optional<ReservationOrderSnapshot> findOrderById(String hotelId, Long orderId) {
        ReservationOrderEntity entity = orderMapper.selectOne(Wrappers.<ReservationOrderEntity>lambdaQuery()
                .eq(ReservationOrderEntity::getHotelId, hotelId)
                .eq(ReservationOrderEntity::getId, orderId)
                .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toOrderSnapshot);
    }

    /**
     * 按业务 key 查询订单上下文。包含 ACTIVE 和已终止订单，供 Skill 判断冲突或终止事实。
     */
    @Override
    public List<ReservationAiQueryOrderSnapshot> findAiQueryOrdersByBusinessKeys(
            String hotelId,
            String groupCode,
            String confirmationNumber) {
        Map<Long, ReservationAiQueryOrderSnapshot> orders = new LinkedHashMap<>();
        if (hasText(groupCode)) {
            orderMapper.selectList(Wrappers.<ReservationOrderEntity>lambdaQuery()
                            .eq(ReservationOrderEntity::getHotelId, hotelId)
                            .eq(ReservationOrderEntity::getOrderKeyType, ReservationOrderKeyType.GROUP_CODE.name())
                            .and(wrapper -> wrapper
                                    .eq(ReservationOrderEntity::getActiveBusinessKey, groupCode)
                                    .or()
                                    .eq(ReservationOrderEntity::getOrderBusinessKey, groupCode))
                            .orderByDesc(ReservationOrderEntity::getUpdatedAt))
                    .forEach(entity -> orders.put(entity.getId(), toAiQueryOrderSnapshot(entity)));
        }
        if (hasText(confirmationNumber)) {
            orderMapper.selectList(Wrappers.<ReservationOrderEntity>lambdaQuery()
                            .eq(ReservationOrderEntity::getHotelId, hotelId)
                            .eq(ReservationOrderEntity::getOrderKeyType, ReservationOrderKeyType.CONFIRMATION_NUMBER.name())
                            .and(wrapper -> wrapper
                                    .eq(ReservationOrderEntity::getActiveBusinessKey, confirmationNumber)
                                    .or()
                                    .eq(ReservationOrderEntity::getOrderBusinessKey, confirmationNumber))
                            .orderByDesc(ReservationOrderEntity::getUpdatedAt))
                    .forEach(entity -> orders.put(entity.getId(), toAiQueryOrderSnapshot(entity)));
        }
        return List.copyOf(orders.values());
    }

    /**
     * 按订单 ID 查询 AI 查询接口使用的订单快照。
     */
    @Override
    public Optional<ReservationAiQueryOrderSnapshot> findAiQueryOrderById(String hotelId, Long orderId) {
        ReservationOrderEntity entity = orderMapper.selectOne(Wrappers.<ReservationOrderEntity>lambdaQuery()
                .eq(ReservationOrderEntity::getHotelId, hotelId)
                .eq(ReservationOrderEntity::getId, orderId)
                .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toAiQueryOrderSnapshot);
    }

    /**
     * 按订单 ID 批量查询订单快照，供前端任务列表补充订单展示键。
     */
    @Override
    public List<ReservationAiQueryOrderSnapshot> findAiQueryOrdersByIds(String hotelId, List<Long> orderIds) {
        if (orderIds == null || orderIds.isEmpty()) {
            return List.of();
        }
        return orderMapper.selectList(Wrappers.<ReservationOrderEntity>lambdaQuery()
                        .eq(ReservationOrderEntity::getHotelId, hotelId)
                        .in(ReservationOrderEntity::getId, orderIds))
                .stream()
                .map(this::toAiQueryOrderSnapshot)
                .toList();
    }

    /**
     * 按来源消息 ID 批量查询订单快照，供 SourceMessage 会话关联摘要使用。
     */
    @Override
    public List<ReservationAiQueryOrderSnapshot> findAiQueryOrdersBySourceMessageIds(
            String hotelId,
            List<Long> sourceMessageIds) {
        if (sourceMessageIds == null || sourceMessageIds.isEmpty()) {
            return List.of();
        }
        return orderMapper.selectList(Wrappers.<ReservationOrderEntity>lambdaQuery()
                        .eq(ReservationOrderEntity::getHotelId, hotelId)
                        .in(ReservationOrderEntity::getSourceMessageId, sourceMessageIds)
                        .ne(ReservationOrderEntity::getOrderVisibility, ReservationOrderVisibility.HIDDEN_SYSTEM.name())
                        .orderByDesc(ReservationOrderEntity::getUpdatedAt))
                .stream()
                .map(this::toAiQueryOrderSnapshot)
                .toList();
    }

    /**
     * 前端订单列表分页查询。状态参数为空时不加状态过滤，默认返回全部订单状态。
     */
    @Override
    public ReservationPageSnapshot<ReservationAiQueryOrderSnapshot> queryFrontendOrders(
            ReservationOrderListQueryRequest request,
            List<Long> keywordSourceMessageIds,
            int pageNum,
            int pageSize) {
        List<Long> sourceMessageIds = keywordSourceMessageIds == null ? List.of() : keywordSourceMessageIds;
        Page<ReservationOrderEntity> page = orderMapper.selectPage(Page.of(pageNum, pageSize),
                Wrappers.<ReservationOrderEntity>lambdaQuery()
                        .eq(ReservationOrderEntity::getHotelId, request.hotelId())
                        .ne(ReservationOrderEntity::getOrderVisibility, ReservationOrderVisibility.HIDDEN_SYSTEM.name())
                        .eq(hasText(request.orderStatus()),
                                ReservationOrderEntity::getOrderStatus,
                                trim(request.orderStatus()))
                        .and(hasText(request.groupCode()), wrapper -> wrapper
                                .like(ReservationOrderEntity::getOrderBusinessKey, trim(request.groupCode()))
                                .or()
                                .like(ReservationOrderEntity::getActiveBusinessKey, trim(request.groupCode()))
                                .or()
                                .like(ReservationOrderEntity::getDisplayName, trim(request.groupCode())))
                        .and(hasText(request.confirmationNumber()), wrapper -> wrapper
                                .like(ReservationOrderEntity::getOrderBusinessKey, trim(request.confirmationNumber()))
                                .or()
                                .like(ReservationOrderEntity::getActiveBusinessKey, trim(request.confirmationNumber()))
                                .or()
                                .like(ReservationOrderEntity::getDisplayName, trim(request.confirmationNumber())))
                        .and(hasText(request.keyword()), wrapper -> wrapper
                                .like(ReservationOrderEntity::getOrderBusinessKey, trim(request.keyword()))
                                .or()
                                .like(ReservationOrderEntity::getActiveBusinessKey, trim(request.keyword()))
                                .or()
                                .like(ReservationOrderEntity::getTemporaryOrderCode, trim(request.keyword()))
                                .or()
                                .like(ReservationOrderEntity::getOrderStatus, trim(request.keyword()))
                                .or()
                                .like(ReservationOrderEntity::getDisplayName, trim(request.keyword()))
                                .or(!sourceMessageIds.isEmpty())
                                .in(!sourceMessageIds.isEmpty(),
                                        ReservationOrderEntity::getSourceMessageId,
                                        sourceMessageIds))
                        .orderByDesc(ReservationOrderEntity::getUpdatedAt)
                        .orderByDesc(ReservationOrderEntity::getId));
        return new ReservationPageSnapshot<>(
                page.getRecords().stream().map(this::toAiQueryOrderSnapshot).toList(),
                page.getTotal(),
                pageNum,
                pageSize);
    }

    /**
     * 前端任务列表分页查询。只读取任务摘要字段，完整任务卡 payload 仍走任务详情接口。
     */
    @Override
    public ReservationPageSnapshot<ReservationAiQueryTaskSnapshot> queryFrontendTasks(
            ReservationTaskWorkbenchQueryRequest request,
            List<Long> keywordSourceMessageIds,
            int pageNum,
            int pageSize) {
        List<Long> keywordOrderIds = findOrderIdsByKeyword(request.hotelId(), request.keyword());
        List<Long> sourceMessageIds = keywordSourceMessageIds == null ? List.of() : keywordSourceMessageIds;
        Page<ReservationTaskEntity> page = taskMapper.selectFrontendTaskPage(
                Page.of(pageNum, pageSize),
                request.hotelId(),
                request.orderId(),
                trim(request.orderStatus()),
                trim(request.taskType()),
                trim(request.taskStatus()),
                trim(request.taskSubtype()),
                request.queueParticipation(),
                trim(request.keyword()),
                keywordOrderIds,
                sourceMessageIds);
        return new ReservationPageSnapshot<>(
                toAiQueryTaskSnapshots(request.hotelId(), page.getRecords()),
                page.getTotal(),
                pageNum,
                pageSize);
    }

    /**
     * 查询指定订单下全部任务，并补充对应 AI transition 的事件序号和 Skill 信息。
     */
    @Override
    public List<ReservationAiQueryTaskSnapshot> findAiQueryTasksByOrderIds(String hotelId, List<Long> orderIds) {
        if (orderIds == null || orderIds.isEmpty()) {
            return List.of();
        }
        List<ReservationTaskEntity> tasks = taskMapper.selectList(Wrappers.<ReservationTaskEntity>lambdaQuery()
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .in(ReservationTaskEntity::getOrderId, orderIds)
                .orderByAsc(ReservationTaskEntity::getOrderId)
                .orderByAsc(ReservationTaskEntity::getExecutionOrder));
        return toAiQueryTaskSnapshots(hotelId, tasks);
    }

    /**
     * 按来源消息 ID 批量查询任务快照，供 SourceMessage 会话关联摘要使用。
     */
    @Override
    public List<ReservationAiQueryTaskSnapshot> findAiQueryTasksBySourceMessageIds(
            String hotelId,
            List<Long> sourceMessageIds) {
        if (sourceMessageIds == null || sourceMessageIds.isEmpty()) {
            return List.of();
        }
        List<ReservationTaskEntity> tasks = taskMapper.selectList(Wrappers.<ReservationTaskEntity>lambdaQuery()
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .in(ReservationTaskEntity::getSourceMessageId, sourceMessageIds)
                .orderByAsc(ReservationTaskEntity::getOrderId)
                .orderByAsc(ReservationTaskEntity::getExecutionOrder));
        return toAiQueryTaskSnapshots(hotelId, tasks);
    }

    /**
     * 按 AI transition 中冗余的 Group Code 或 Confirmation Number 查询任务。
     */
    @Override
    public List<ReservationAiQueryTaskSnapshot> findAiQueryTasksByBusinessKeys(
            String hotelId,
            String groupCode,
            String confirmationNumber) {
        List<ReservationAiTransitionEntity> transitions = findAiQueryTransitionsByBusinessKeys(
                hotelId,
                groupCode,
                confirmationNumber);
        if (transitions.isEmpty()) {
            return List.of();
        }
        List<Long> transitionIds = transitions.stream().map(ReservationAiTransitionEntity::getId).toList();
        List<ReservationTaskEntity> tasks = taskMapper.selectList(Wrappers.<ReservationTaskEntity>lambdaQuery()
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .in(ReservationTaskEntity::getAiTransitionId, transitionIds)
                .orderByAsc(ReservationTaskEntity::getOrderId)
                .orderByAsc(ReservationTaskEntity::getExecutionOrder));
        return toAiQueryTaskSnapshots(hotelId, tasks);
    }

    /**
     * 将临时订单激活为有真实业务号的 ACTIVE 订单。唯一约束冲突由 Service 转换为业务错误。
     */
    @Override
    public boolean activateTemporaryOrderWithBusinessKey(
            String hotelId,
            Long orderId,
            String orderKeyType,
            String orderBusinessKey,
            String businessKeySource,
            LocalDateTime now) {
        return orderMapper.update(null, Wrappers.<ReservationOrderEntity>lambdaUpdate()
                .set(ReservationOrderEntity::getOrderKeyType, orderKeyType)
                .set(ReservationOrderEntity::getOrderBusinessKey, orderBusinessKey)
                .set(ReservationOrderEntity::getActiveBusinessKey, orderBusinessKey)
                .set(ReservationOrderEntity::getOrderStatus, ReservationOrderStatus.ACTIVE.name())
                .set(ReservationOrderEntity::getBusinessKeySource, businessKeySource)
                .set(ReservationOrderEntity::getDisplayName, orderBusinessKey)
                .set(ReservationOrderEntity::getUpdatedAt, now)
                .eq(ReservationOrderEntity::getHotelId, hotelId)
                .eq(ReservationOrderEntity::getId, orderId)
                .eq(ReservationOrderEntity::getOrderStatus, ReservationOrderStatus.TEMPORARY.name())) > 0;
    }

    /**
     * 更新人工复核任务转换后的类型和订单归属。
     */
    @Override
    public void updateTaskForManualConversion(
            String hotelId,
            Long taskId,
            Long orderId,
            String aiTaskType,
            String systemTaskType,
            String taskCardType,
            Integer executionOrder,
            LocalDateTime now) {
        ReservationTaskEntity entity = new ReservationTaskEntity();
        entity.setOrderId(orderId);
        entity.setAiTaskType(aiTaskType);
        entity.setSystemTaskType(systemTaskType);
        entity.setTaskCardType(taskCardType);
        entity.setExecutionOrder(executionOrder);
        entity.setUpdatedAt(now);
        taskMapper.update(entity, Wrappers.<ReservationTaskEntity>lambdaUpdate()
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .eq(ReservationTaskEntity::getId, taskId));
        ReservationTaskCardEntity cardEntity = new ReservationTaskCardEntity();
        cardEntity.setTaskCardType(taskCardType);
        cardEntity.setUpdatedAt(now);
        taskCardMapper.update(cardEntity, Wrappers.<ReservationTaskCardEntity>lambdaUpdate()
                .eq(ReservationTaskCardEntity::getHotelId, hotelId)
                .eq(ReservationTaskCardEntity::getTaskId, taskId));
    }

    /**
     * 保存任务卡草稿 payload。
     */
    @Override
    public boolean updateTaskDraftPayload(String hotelId, Long taskId, String draftPayloadJson, LocalDateTime now) {
        return taskCardMapper.update(null, Wrappers.<ReservationTaskCardEntity>lambdaUpdate()
                .set(ReservationTaskCardEntity::getDraftPayloadJson, draftPayloadJson)
                .set(ReservationTaskCardEntity::getUpdatedAt, now)
                .eq(ReservationTaskCardEntity::getHotelId, hotelId)
                .eq(ReservationTaskCardEntity::getTaskId, taskId)) > 0;
    }

    /**
     * 保存最终确认 payload，并将任务状态从 PENDING_CONFIRM 流转为 READY。
     */
    @Override
    public boolean confirmTaskPayload(
            String hotelId,
            Long taskId,
            String confirmedPayloadJson,
            String confirmedBy,
            LocalDateTime confirmedAt) {
        int updatedTaskCount = taskMapper.update(null, Wrappers.<ReservationTaskEntity>lambdaUpdate()
                .set(ReservationTaskEntity::getTaskStatus, ReservationTaskStatus.READY.name())
                .set(ReservationTaskEntity::getConfirmedAt, confirmedAt)
                .set(ReservationTaskEntity::getUpdatedAt, confirmedAt)
                .setSql("version = version + 1")
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .eq(ReservationTaskEntity::getId, taskId)
                .eq(ReservationTaskEntity::getTaskStatus, ReservationTaskStatus.PENDING_CONFIRM.name()));
        if (updatedTaskCount == 0) {
            return false;
        }
        int updatedCardCount = taskCardMapper.update(null, Wrappers.<ReservationTaskCardEntity>lambdaUpdate()
                .set(ReservationTaskCardEntity::getConfirmedPayloadJson, confirmedPayloadJson)
                .set(ReservationTaskCardEntity::getConfirmedBy, confirmedBy)
                .set(ReservationTaskCardEntity::getConfirmedAt, confirmedAt)
                .set(ReservationTaskCardEntity::getUpdatedAt, confirmedAt)
                .eq(ReservationTaskCardEntity::getHotelId, hotelId)
                .eq(ReservationTaskCardEntity::getTaskId, taskId));
        return updatedCardCount > 0;
    }

    /**
     * 保存同卡人工复核解阻结果和最终确认 payload，并将任务状态从 PENDING_CONFIRM 流转为 READY。
     */
    @Override
    public boolean resolveManualReview(
            String hotelId,
            Long taskId,
            String reviewStatus,
            String reviewResolutionJson,
            String confirmedPayloadJson,
            String confirmedBy,
            LocalDateTime confirmedAt) {
        int updatedTaskCount = taskMapper.update(null, Wrappers.<ReservationTaskEntity>lambdaUpdate()
                .set(ReservationTaskEntity::getTaskStatus, ReservationTaskStatus.READY.name())
                .set(ReservationTaskEntity::getConfirmedAt, confirmedAt)
                .set(ReservationTaskEntity::getUpdatedAt, confirmedAt)
                .setSql("version = version + 1")
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .eq(ReservationTaskEntity::getId, taskId)
                .eq(ReservationTaskEntity::getTaskStatus, ReservationTaskStatus.PENDING_CONFIRM.name()));
        if (updatedTaskCount == 0) {
            return false;
        }
        int updatedCardCount = taskCardMapper.update(null, Wrappers.<ReservationTaskCardEntity>lambdaUpdate()
                .set(ReservationTaskCardEntity::getReviewStatus, reviewStatus)
                .set(ReservationTaskCardEntity::getReviewResolutionJson, reviewResolutionJson)
                .set(ReservationTaskCardEntity::getConfirmedPayloadJson, confirmedPayloadJson)
                .set(ReservationTaskCardEntity::getConfirmedBy, confirmedBy)
                .set(ReservationTaskCardEntity::getConfirmedAt, confirmedAt)
                .set(ReservationTaskCardEntity::getUpdatedAt, confirmedAt)
                .eq(ReservationTaskCardEntity::getHotelId, hotelId)
                .eq(ReservationTaskCardEntity::getTaskId, taskId));
        return updatedCardCount > 0;
    }

    /**
     * 插入 OPERA 模拟操作。
     */
    @Override
    public Long insertOperaOperation(ReservationOperaOperationDraft draft) {
        ReservationOperaOperationEntity entity = new ReservationOperaOperationEntity();
        entity.setHotelId(draft.hotelId());
        entity.setOrderId(draft.orderId());
        entity.setTaskId(draft.taskId());
        entity.setOperationSequence(draft.operationSequence());
        entity.setOperationCode(draft.operationCode());
        entity.setOperationName(draft.operationName());
        entity.setOperationStatus(draft.operationStatus());
        entity.setRequestPayloadJson(draft.requestPayloadJson());
        entity.setAttemptCount(0);
        entity.setCreatedAt(draft.now());
        entity.setUpdatedAt(draft.now());
        operaOperationMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 查询任务下 OPERA 模拟操作，按任务内顺序返回。
     */
    @Override
    public List<ReservationOperaOperationSnapshot> findOperaOperationsByTaskId(String hotelId, Long taskId) {
        return operaOperationMapper.selectList(Wrappers.<ReservationOperaOperationEntity>lambdaQuery()
                        .eq(ReservationOperaOperationEntity::getHotelId, hotelId)
                        .eq(ReservationOperaOperationEntity::getTaskId, taskId)
                        .orderByAsc(ReservationOperaOperationEntity::getOperationSequence))
                .stream()
                .map(this::toOperaOperationSnapshot)
                .toList();
    }

    /**
     * 按操作 ID 查询 OPERA 模拟操作。
     */
    @Override
    public Optional<ReservationOperaOperationSnapshot> findOperaOperationById(String hotelId, Long operationId) {
        ReservationOperaOperationEntity entity = operaOperationMapper.selectOne(
                Wrappers.<ReservationOperaOperationEntity>lambdaQuery()
                        .eq(ReservationOperaOperationEntity::getHotelId, hotelId)
                        .eq(ReservationOperaOperationEntity::getId, operationId)
                        .last("LIMIT 1"));
        return Optional.ofNullable(entity).map(this::toOperaOperationSnapshot);
    }

    /**
     * 查询 OPERA 模拟操作 attempt 历史。
     */
    @Override
    public List<ReservationOperaOperationAttemptSnapshot> findOperaOperationAttempts(String hotelId, Long operationId) {
        return operaOperationAttemptMapper.selectList(Wrappers.<ReservationOperaOperationAttemptEntity>lambdaQuery()
                        .eq(ReservationOperaOperationAttemptEntity::getHotelId, hotelId)
                        .eq(ReservationOperaOperationAttemptEntity::getOperationId, operationId)
                        .orderByAsc(ReservationOperaOperationAttemptEntity::getAttemptNumber))
                .stream()
                .map(this::toOperaOperationAttemptSnapshot)
                .toList();
    }

    /**
     * 插入 OPERA 模拟操作 attempt。
     */
    @Override
    public Long insertOperaOperationAttempt(ReservationOperaOperationAttemptDraft draft) {
        ReservationOperaOperationAttemptEntity entity = new ReservationOperaOperationAttemptEntity();
        entity.setHotelId(draft.hotelId());
        entity.setOrderId(draft.orderId());
        entity.setTaskId(draft.taskId());
        entity.setOperationId(draft.operationId());
        entity.setAttemptNumber(draft.attemptNumber());
        entity.setAttemptStatus(draft.attemptStatus());
        entity.setRequestPayloadJson(draft.requestPayloadJson());
        entity.setResponsePayloadJson(draft.responsePayloadJson());
        entity.setErrorMessage(draft.errorMessage());
        entity.setStartedAt(draft.startedAt());
        entity.setFinishedAt(draft.finishedAt());
        entity.setCreatedAt(draft.startedAt());
        operaOperationAttemptMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 更新 OPERA 模拟操作当前状态和最近 attempt 摘要。
     */
    @Override
    public boolean updateOperaOperationAfterAttempt(
            String hotelId,
            Long operationId,
            String operationStatus,
            Long lastAttemptId,
            String lastErrorMessage,
            LocalDateTime now) {
        return operaOperationMapper.update(null, Wrappers.<ReservationOperaOperationEntity>lambdaUpdate()
                .set(ReservationOperaOperationEntity::getOperationStatus, operationStatus)
                .set(ReservationOperaOperationEntity::getLastAttemptId, lastAttemptId)
                .set(ReservationOperaOperationEntity::getLastErrorMessage, lastErrorMessage)
                .set(ReservationOperaOperationEntity::getUpdatedAt, now)
                .setSql("attempt_count = attempt_count + 1")
                .eq(ReservationOperaOperationEntity::getHotelId, hotelId)
                .eq(ReservationOperaOperationEntity::getId, operationId)) > 0;
    }

    /**
     * 更新任务状态。
     */
    @Override
    public boolean updateTaskStatus(String hotelId, Long taskId, String taskStatus, LocalDateTime now) {
        return taskMapper.update(null, Wrappers.<ReservationTaskEntity>lambdaUpdate()
                .set(ReservationTaskEntity::getTaskStatus, taskStatus)
                .set(ReservationTaskEntity::getCompletedAt,
                        ReservationTaskStatus.COMPLETED.name().equals(taskStatus) ? now : null)
                .set(ReservationTaskEntity::getUpdatedAt, now)
                .setSql("version = version + 1")
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .eq(ReservationTaskEntity::getId, taskId)) > 0;
    }

    /**
     * 统计订单下全部任务数量。
     */
    @Override
    public int countTasksByOrder(String hotelId, Long orderId) {
        return Math.toIntExact(taskMapper.selectCount(Wrappers.<ReservationTaskEntity>lambdaQuery()
                .eq(ReservationTaskEntity::getHotelId, hotelId)
                .eq(ReservationTaskEntity::getOrderId, orderId)));
    }

    /**
     * 将订单逻辑删除。该方法只做状态更新，是否空订单由 Service 决策。
     */
    @Override
    public boolean logicDeleteOrder(String hotelId, Long orderId, String reason, LocalDateTime now) {
        return orderMapper.update(null, Wrappers.<ReservationOrderEntity>lambdaUpdate()
                .set(ReservationOrderEntity::getOrderStatus, ReservationOrderStatus.LOGIC_DELETED.name())
                .set(ReservationOrderEntity::getActiveBusinessKey, null)
                .set(ReservationOrderEntity::getLogicDeletedAt, now)
                .set(ReservationOrderEntity::getLogicDeletedReason, reason)
                .set(ReservationOrderEntity::getUpdatedAt, now)
                .eq(ReservationOrderEntity::getHotelId, hotelId)
                .eq(ReservationOrderEntity::getId, orderId)
                .ne(ReservationOrderEntity::getOrderStatus, ReservationOrderStatus.LOGIC_DELETED.name())) > 0;
    }

    /**
     * 插入审计日志，记录人工转换和订单迁移摘要。
     */
    @Override
    public Long insertAuditLog(ReservationAuditLogDraft draft) {
        ReservationAuditLogEntity entity = new ReservationAuditLogEntity();
        entity.setHotelId(draft.hotelId());
        entity.setOrderId(draft.orderId());
        entity.setTaskId(draft.taskId());
        entity.setOperationId(draft.operationId());
        entity.setActorType(draft.actorType());
        entity.setActorId(draft.actorId());
        entity.setAction(draft.action());
        entity.setReason(draft.reason());
        entity.setBeforeSnapshotJson(draft.beforeSnapshotJson());
        entity.setAfterSnapshotJson(draft.afterSnapshotJson());
        entity.setOccurredAt(draft.occurredAt());
        entity.setCreatedAt(draft.occurredAt());
        auditLogMapper.insert(entity);
        return entity.getId();
    }

    /**
     * 查询任务审计流水，按发生时间正序返回。
     */
    @Override
    public List<ReservationAuditLogSnapshot> findAuditLogsByTaskId(String hotelId, Long taskId) {
        return auditLogMapper.selectList(Wrappers.<ReservationAuditLogEntity>lambdaQuery()
                        .eq(ReservationAuditLogEntity::getHotelId, hotelId)
                        .eq(ReservationAuditLogEntity::getTaskId, taskId)
                        .orderByAsc(ReservationAuditLogEntity::getOccurredAt))
                .stream()
                .map(this::toAuditLogSnapshot)
                .toList();
    }

    /**
     * 按业务 key 查询 AI transition。该查询只用于补充待处理任务上下文，不替代订单查询。
     */
    private List<ReservationAiTransitionEntity> findAiQueryTransitionsByBusinessKeys(
            String hotelId,
            String groupCode,
            String confirmationNumber) {
        if (!hasText(groupCode) && !hasText(confirmationNumber)) {
            return List.of();
        }
        return transitionMapper.selectList(Wrappers.<ReservationAiTransitionEntity>lambdaQuery()
                .eq(ReservationAiTransitionEntity::getHotelId, hotelId)
                .and(wrapper -> {
                    boolean hasPrevious = false;
                    if (hasText(groupCode)) {
                        wrapper.eq(ReservationAiTransitionEntity::getGroupCode, groupCode);
                        hasPrevious = true;
                    }
                    if (hasText(confirmationNumber)) {
                        if (hasPrevious) {
                            wrapper.or();
                        }
                        wrapper.eq(ReservationAiTransitionEntity::getConfirmationNumber, confirmationNumber);
                    }
                }));
    }

    /**
     * 将任务实体批量转换为 AI 查询任务快照，补充 transition 中的 Skill 元数据。
     */
    private List<ReservationAiQueryTaskSnapshot> toAiQueryTaskSnapshots(
            String hotelId,
            List<ReservationTaskEntity> tasks) {
        if (tasks == null || tasks.isEmpty()) {
            return List.of();
        }
        Map<Long, ReservationAiTransitionEntity> transitions = findTransitionsByIds(
                hotelId,
                tasks.stream().map(ReservationTaskEntity::getAiTransitionId).toList());
        return tasks.stream()
                .map(task -> toAiQueryTaskSnapshot(task, transitions.get(task.getAiTransitionId())))
                .toList();
    }

    /**
     * 按 transition ID 批量查询，并按 ID 建立索引。
     */
    private Map<Long, ReservationAiTransitionEntity> findTransitionsByIds(String hotelId, Collection<Long> transitionIds) {
        if (transitionIds == null || transitionIds.isEmpty()) {
            return Map.of();
        }
        Map<Long, ReservationAiTransitionEntity> result = new LinkedHashMap<>();
        transitionMapper.selectList(Wrappers.<ReservationAiTransitionEntity>lambdaQuery()
                        .eq(ReservationAiTransitionEntity::getHotelId, hotelId)
                        .in(ReservationAiTransitionEntity::getId, transitionIds))
                .forEach(entity -> result.put(entity.getId(), entity));
        return result;
    }

    /**
     * 判断文本是否可参与查询条件。
     */
    private boolean hasText(String value) {
        return value != null && !value.isBlank();
    }

    /**
     * 去除查询文本首尾空白，避免空格参与等值查询。
     */
    private String trim(String value) {
        return value == null ? null : value.trim();
    }

    /**
     * 按订单业务号、临时订单号和展示名查询订单 ID，用于任务列表关键词过滤。
     */
    private List<Long> findOrderIdsByKeyword(String hotelId, String keyword) {
        if (!hasText(keyword)) {
            return List.of();
        }
        String trimmedKeyword = trim(keyword);
        return orderMapper.selectList(Wrappers.<ReservationOrderEntity>lambdaQuery()
                        .eq(ReservationOrderEntity::getHotelId, hotelId)
                        .and(wrapper -> wrapper
                                .like(ReservationOrderEntity::getOrderBusinessKey, trimmedKeyword)
                                .or()
                                .like(ReservationOrderEntity::getActiveBusinessKey, trimmedKeyword)
                                .or()
                                .like(ReservationOrderEntity::getTemporaryOrderCode, trimmedKeyword)
                                .or()
                                .like(ReservationOrderEntity::getDisplayName, trimmedKeyword)))
                .stream()
                .map(ReservationOrderEntity::getId)
                .toList();
    }

    /**
     * 转换批次实体为快照。
     */
    private ReservationAiBatchSnapshot toBatchSnapshot(ReservationAiBatchEntity entity) {
        return new ReservationAiBatchSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getSourceMessageId(),
                entity.getRequestPayloadSha256(),
                entity.getBatchIdempotencyKey(),
                entity.getItemCount());
    }

    /**
     * 转换订单实体为快照。
     */
    private ReservationOrderSnapshot toOrderSnapshot(ReservationOrderEntity entity) {
        return new ReservationOrderSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderKeyType(),
                entity.getOrderBusinessKey(),
                entity.getActiveBusinessKey(),
                entity.getTemporaryOrderCode(),
                entity.getOrderStatus(),
                entity.getOrderVisibility());
    }

    /**
     * 转换订单实体为 AI 查询订单快照。
     */
    private ReservationAiQueryOrderSnapshot toAiQueryOrderSnapshot(ReservationOrderEntity entity) {
        return new ReservationAiQueryOrderSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderKeyType(),
                entity.getOrderBusinessKey(),
                entity.getActiveBusinessKey(),
                entity.getTemporaryOrderCode(),
                entity.getOrderStatus(),
                entity.getOrderVisibility(),
                entity.getBusinessKeySource(),
                entity.getDisplayName(),
                entity.getSourceMessageId(),
                entity.getCreatedFromTaskId(),
                entity.getEndedAt(),
                entity.getLogicDeletedAt(),
                entity.getLogicDeletedReason(),
                entity.getCreatedAt(),
                entity.getUpdatedAt());
    }

    /**
     * 转换任务实体为快照。
     */
    private ReservationTaskSnapshot toTaskSnapshot(ReservationTaskEntity entity) {
        return new ReservationTaskSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderId(),
                entity.getSourceMessageId(),
                entity.getAiTransitionId(),
                entity.getResultType(),
                entity.getAiTaskType(),
                entity.getSystemTaskType(),
                entity.getTaskCardType(),
                entity.getTaskSubtype(),
                entity.getTaskStatus(),
                entity.getQueueParticipation(),
                entity.getExecutionOrder(),
                entity.getParentTaskId(),
                entity.getParentSourceEventIndex(),
                entity.getLinkedTaskGroupId(),
                entity.getBlockedUntilParentCompleted(),
                entity.getConfirmedAt());
    }

    /**
     * 转换任务实体为 AI 查询任务快照，transition 为空时仍返回任务自身事实。
     */
    private ReservationAiQueryTaskSnapshot toAiQueryTaskSnapshot(
            ReservationTaskEntity entity,
            ReservationAiTransitionEntity transition) {
        return new ReservationAiQueryTaskSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderId(),
                entity.getSourceMessageId(),
                entity.getAiTransitionId(),
                entity.getResultType(),
                entity.getAiTaskType(),
                entity.getSystemTaskType(),
                entity.getTaskCardType(),
                entity.getTaskSubtype(),
                entity.getTaskStatus(),
                entity.getQueueParticipation(),
                entity.getExecutionOrder(),
                entity.getParentTaskId(),
                entity.getParentSourceEventIndex(),
                entity.getLinkedTaskGroupId(),
                entity.getBlockedUntilParentCompleted(),
                entity.getLastFailureReason(),
                entity.getCompletedAt(),
                entity.getCreatedAt(),
                entity.getUpdatedAt(),
                transition == null ? null : transition.getSourceEventIndex(),
                transition == null ? null : transition.getCatalogCode(),
                transition == null ? null : transition.getSkillId(),
                transition == null ? null : transition.getRouteCode(),
                transition == null ? null : transition.getSystemProcessCategory(),
                transition == null ? null : transition.getAdapterErrorCode(),
                transition == null ? null : transition.getAdapterErrorMessage());
    }

    /**
     * 转换 AI transition 实体为展示快照。
     */
    private ReservationAiTransitionSnapshot toAiTransitionSnapshot(ReservationAiTransitionEntity entity) {
        return new ReservationAiTransitionSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getBatchId(),
                entity.getSourceMessageId(),
                entity.getSourceEventIndex(),
                entity.getArrayIndex(),
                entity.getExecutionOrder(),
                entity.getCatalogCode(),
                entity.getSkillId(),
                entity.getResultType(),
                entity.getAiTaskType(),
                entity.getRouteCode(),
                entity.getSystemProcessCategory(),
                entity.getSystemTaskType(),
                entity.getTaskCardType(),
                entity.getTaskSubtype(),
                entity.getCurrentOrHistory(),
                entity.getAdapterErrorCode(),
                entity.getAdapterErrorMessage(),
                entity.getAiPayloadJson(),
                entity.getCreatedAt(),
                entity.getUpdatedAt());
    }

    /**
     * 转换任务卡实体为快照。
     */
    private ReservationTaskCardSnapshot toTaskCardSnapshot(ReservationTaskCardEntity entity) {
        return new ReservationTaskCardSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getTaskId(),
                entity.getTaskCardType(),
                entity.getFieldContractVersion(),
                entity.getAiPayloadJson(),
                entity.getDraftPayloadJson(),
                entity.getConfirmedPayloadJson(),
                entity.getConfirmedBy(),
                entity.getConfirmedAt(),
                entity.getReviewStatus(),
                entity.getReviewResolutionJson());
    }

    /**
     * 转换 OPERA 模拟操作实体为快照。
     */
    private ReservationOperaOperationSnapshot toOperaOperationSnapshot(ReservationOperaOperationEntity entity) {
        return new ReservationOperaOperationSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderId(),
                entity.getTaskId(),
                entity.getOperationSequence(),
                entity.getOperationCode(),
                entity.getOperationName(),
                entity.getOperationStatus(),
                entity.getRequestPayloadJson(),
                entity.getAttemptCount(),
                entity.getLastAttemptId(),
                entity.getLastErrorMessage(),
                entity.getCreatedAt(),
                entity.getUpdatedAt());
    }

    /**
     * 转换 OPERA 模拟操作 attempt 实体为快照。
     */
    private ReservationOperaOperationAttemptSnapshot toOperaOperationAttemptSnapshot(
            ReservationOperaOperationAttemptEntity entity) {
        return new ReservationOperaOperationAttemptSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderId(),
                entity.getTaskId(),
                entity.getOperationId(),
                entity.getAttemptNumber(),
                entity.getAttemptStatus(),
                entity.getRequestPayloadJson(),
                entity.getResponsePayloadJson(),
                entity.getErrorMessage(),
                entity.getStartedAt(),
                entity.getFinishedAt());
    }

    /**
     * 转换审计实体为快照。
     */
    private ReservationAuditLogSnapshot toAuditLogSnapshot(ReservationAuditLogEntity entity) {
        return new ReservationAuditLogSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getOrderId(),
                entity.getTaskId(),
                entity.getOperationId(),
                entity.getActorType(),
                entity.getActorId(),
                entity.getAction(),
                entity.getReason(),
                entity.getBeforeSnapshotJson(),
                entity.getAfterSnapshotJson(),
                entity.getOccurredAt());
    }
}
