package cn.nianxx.thhotel.integrations.ai.superagent.repository;

import cn.nianxx.thhotel.integrations.ai.superagent.common.dto.SuperAgentDispatchRunDraft;
import cn.nianxx.thhotel.integrations.ai.superagent.common.dto.SuperAgentDispatchRunFailureUpdate;
import cn.nianxx.thhotel.integrations.ai.superagent.common.dto.SuperAgentDispatchRunSnapshot;
import cn.nianxx.thhotel.integrations.ai.superagent.common.dto.SuperAgentDispatchRunSuccessUpdate;
import cn.nianxx.thhotel.integrations.ai.superagent.common.enums.SuperAgentDispatchStatus;
import cn.nianxx.thhotel.integrations.ai.superagent.domain.SuperAgentDispatchRunEntity;
import cn.nianxx.thhotel.integrations.ai.superagent.mapper.SuperAgentDispatchRunMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;

/**
 * SuperAgent 自动分发运行 MyBatis-Plus 持久化实现。数据库并发和幂等细节留在本层。
 */
@Repository
public class MybatisSuperAgentDispatchRunRepository implements SuperAgentDispatchRunRepository {

    private final SuperAgentDispatchRunMapper mapper;

    /**
     * 注入 SuperAgent dispatch run Mapper。
     */
    public MybatisSuperAgentDispatchRunRepository(SuperAgentDispatchRunMapper mapper) {
        this.mapper = mapper;
    }

    /**
     * 幂等写入 dispatch run；重复键命中时回查已有记录，避免 AgentBus 重复投递重复分发。
     */
    @Override
    public Long insertIfAbsent(SuperAgentDispatchRunDraft draft) {
        SuperAgentDispatchRunEntity entity = toEntity(draft);
        try {
            mapper.insert(entity);
            return entity.getId();
        } catch (DuplicateKeyException exception) {
            return findBySourceMessageAndDispatchSource(draft.sourceMessageId(), draft.dispatchSource())
                    .map(SuperAgentDispatchRunSnapshot::id)
                    .orElseThrow(() -> exception);
        }
    }

    /**
     * 抢占 PENDING/RETRYABLE_FAILED 或锁已过期的 RUNNING 记录。先查候选再逐条条件更新，降低多 worker 重复执行概率。
     */
    @Override
    public List<SuperAgentDispatchRunSnapshot> claimDue(
            String workerId,
            List<String> statuses,
            LocalDateTime now,
            LocalDateTime lockedUntil,
            int limit) {
        if (statuses == null || statuses.isEmpty() || now == null || lockedUntil == null || limit < 1) {
            return List.of();
        }
        List<SuperAgentDispatchRunEntity> candidates = mapper.selectPage(Page.of(1, limit),
                Wrappers.<SuperAgentDispatchRunEntity>lambdaQuery()
                        .in(SuperAgentDispatchRunEntity::getDispatchStatus, statuses)
                        .le(SuperAgentDispatchRunEntity::getNextAttemptAt, now)
                        .and(wrapper -> wrapper
                                .isNull(SuperAgentDispatchRunEntity::getLockedUntil)
                                .or()
                                .lt(SuperAgentDispatchRunEntity::getLockedUntil, now))
                        .orderByAsc(SuperAgentDispatchRunEntity::getNextAttemptAt)
                        .orderByAsc(SuperAgentDispatchRunEntity::getId))
                .getRecords();
        return candidates.stream()
                .map(candidate -> claimOne(candidate, now, lockedUntil))
                .flatMap(Optional::stream)
                .toList();
    }

    /**
     * 按 ID 查询运行快照。
     */
    @Override
    public Optional<SuperAgentDispatchRunSnapshot> findById(Long id) {
        if (id == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(mapper.selectById(id)).map(this::toSnapshot);
    }

    /**
     * 更新成功结果并释放 worker 锁。
     */
    @Override
    public void markSucceeded(SuperAgentDispatchRunSuccessUpdate update) {
        mapper.update(null, Wrappers.<SuperAgentDispatchRunEntity>lambdaUpdate()
                .eq(SuperAgentDispatchRunEntity::getId, update.id())
                .set(SuperAgentDispatchRunEntity::getDispatchStatus, SuperAgentDispatchStatus.SUCCEEDED.code())
                .set(SuperAgentDispatchRunEntity::getSuperagentSessionId, update.superagentSessionId())
                .set(SuperAgentDispatchRunEntity::getSuperagentRunId, update.superagentRunId())
                .set(SuperAgentDispatchRunEntity::getSuperagentRunUri, update.superagentRunUri())
                .set(SuperAgentDispatchRunEntity::getLastEventId, update.lastEventId())
                .set(SuperAgentDispatchRunEntity::getSuperagentRawAnswer, update.superagentRawAnswer())
                .set(SuperAgentDispatchRunEntity::getSuperagentParsedJson, update.superagentParsedJson())
                .set(SuperAgentDispatchRunEntity::getSuperagentTraceJson, update.superagentTraceJson())
                .set(SuperAgentDispatchRunEntity::getSafeErrorCode, null)
                .set(SuperAgentDispatchRunEntity::getSafeErrorSummary, null)
                .set(SuperAgentDispatchRunEntity::getNextAttemptAt, null)
                .set(SuperAgentDispatchRunEntity::getLockedUntil, null)
                .set(SuperAgentDispatchRunEntity::getUpdatedAt, update.updatedAt()));
    }

    /**
     * 更新失败状态和安全错误摘要，并释放 worker 锁。
     */
    @Override
    public void markFailed(SuperAgentDispatchRunFailureUpdate update) {
        mapper.update(null, Wrappers.<SuperAgentDispatchRunEntity>lambdaUpdate()
                .eq(SuperAgentDispatchRunEntity::getId, update.id())
                .set(SuperAgentDispatchRunEntity::getDispatchStatus, update.dispatchStatus())
                .set(SuperAgentDispatchRunEntity::getSafeErrorCode, update.safeErrorCode())
                .set(SuperAgentDispatchRunEntity::getSafeErrorSummary, update.safeErrorSummary())
                .set(SuperAgentDispatchRunEntity::getNextAttemptAt, update.nextAttemptAt())
                .set(SuperAgentDispatchRunEntity::getLockedUntil, null)
                .set(SuperAgentDispatchRunEntity::getUpdatedAt, update.updatedAt()));
    }

    /**
     * 通过 SourceMessage 和分发来源回查幂等记录。
     */
    private Optional<SuperAgentDispatchRunSnapshot> findBySourceMessageAndDispatchSource(
            Long sourceMessageId,
            String dispatchSource) {
        if (sourceMessageId == null || dispatchSource == null || dispatchSource.isBlank()) {
            return Optional.empty();
        }
        SuperAgentDispatchRunEntity entity = mapper.selectOne(Wrappers.<SuperAgentDispatchRunEntity>lambdaQuery()
                .eq(SuperAgentDispatchRunEntity::getSourceMessageId, sourceMessageId)
                .eq(SuperAgentDispatchRunEntity::getDispatchSource, dispatchSource));
        return Optional.ofNullable(entity).map(this::toSnapshot);
    }

    /**
     * 条件抢占单条记录，抢占成功后回读最新快照。
     */
    private Optional<SuperAgentDispatchRunSnapshot> claimOne(
            SuperAgentDispatchRunEntity candidate,
            LocalDateTime now,
            LocalDateTime lockedUntil) {
        int updated = mapper.update(null, Wrappers.<SuperAgentDispatchRunEntity>lambdaUpdate()
                .eq(SuperAgentDispatchRunEntity::getId, candidate.getId())
                .eq(SuperAgentDispatchRunEntity::getDispatchStatus, candidate.getDispatchStatus())
                .le(SuperAgentDispatchRunEntity::getNextAttemptAt, now)
                .and(wrapper -> wrapper
                        .isNull(SuperAgentDispatchRunEntity::getLockedUntil)
                        .or()
                        .lt(SuperAgentDispatchRunEntity::getLockedUntil, now))
                .set(SuperAgentDispatchRunEntity::getDispatchStatus, SuperAgentDispatchStatus.RUNNING.code())
                .set(SuperAgentDispatchRunEntity::getLockedUntil, lockedUntil)
                .set(SuperAgentDispatchRunEntity::getUpdatedAt, now)
                .setSql("attempt_count = attempt_count + 1"));
        if (updated != 1) {
            return Optional.empty();
        }
        return findById(candidate.getId());
    }

    /**
     * 将入库草稿转换为数据库实体。
     */
    private SuperAgentDispatchRunEntity toEntity(SuperAgentDispatchRunDraft draft) {
        SuperAgentDispatchRunEntity entity = new SuperAgentDispatchRunEntity();
        entity.setHotelId(draft.hotelId());
        entity.setSourceMessageId(draft.sourceMessageId());
        entity.setSourceProvider(draft.sourceProvider());
        entity.setSourceChannel(draft.sourceChannel());
        entity.setExternalMessageId(draft.externalMessageId());
        entity.setExternalConversationId(draft.externalConversationId());
        entity.setDispatchSource(draft.dispatchSource());
        entity.setIdempotencyKey(draft.idempotencyKey());
        entity.setRequestId(draft.requestId());
        entity.setDispatchStatus(draft.dispatchStatus());
        entity.setAttemptCount(0);
        entity.setMaxAttempts(draft.maxAttempts());
        entity.setNextAttemptAt(draft.nextAttemptAt());
        entity.setCreatedAt(draft.createdAt());
        entity.setUpdatedAt(draft.createdAt());
        return entity;
    }

    /**
     * 将数据库实体转换为 Service 快照。
     */
    private SuperAgentDispatchRunSnapshot toSnapshot(SuperAgentDispatchRunEntity entity) {
        return new SuperAgentDispatchRunSnapshot(
                entity.getId(),
                entity.getHotelId(),
                entity.getSourceMessageId(),
                entity.getSourceProvider(),
                entity.getSourceChannel(),
                entity.getExternalMessageId(),
                entity.getExternalConversationId(),
                entity.getDispatchSource(),
                entity.getIdempotencyKey(),
                entity.getRequestId(),
                entity.getDispatchStatus(),
                valueOrZero(entity.getAttemptCount()),
                valueOrZero(entity.getMaxAttempts()),
                entity.getNextAttemptAt(),
                entity.getLockedUntil(),
                entity.getSuperagentSessionId(),
                entity.getSuperagentRunId(),
                entity.getSuperagentRunUri(),
                entity.getLastEventId(),
                entity.getSuperagentRawAnswer(),
                entity.getSuperagentParsedJson(),
                entity.getSuperagentTraceJson(),
                entity.getSafeErrorCode(),
                entity.getSafeErrorSummary(),
                entity.getCreatedAt(),
                entity.getUpdatedAt());
    }

    /**
     * 数据库历史空值按 0 处理，避免 worker 计算尝试次数时报空指针。
     */
    private int valueOrZero(Integer value) {
        return value == null ? 0 : value;
    }
}
