package cn.nianxx.thhotel.integrations.ai.superagent.service.impl;

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.SuperAgentDispatchSource;
import cn.nianxx.thhotel.integrations.ai.superagent.common.enums.SuperAgentDispatchStatus;
import cn.nianxx.thhotel.integrations.ai.superagent.common.request.SuperAgentOpenApiMessageRequest;
import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpenApiResult;
import cn.nianxx.thhotel.integrations.ai.superagent.repository.SuperAgentDispatchRunRepository;
import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentDispatchService;
import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentOpenApiClient;
import cn.nianxx.thhotel.platform.message.common.request.CaptureSourceMessageCommand;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageCaptureResult;
import cn.nianxx.thhotel.platform.message.repository.SourceMessageInboxRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

/**
 * AgentBus SourceMessage 自动分发 SuperAgent 服务实现。只负责 outbox 调度，不创建订单或任务。
 */
@Service
public class SuperAgentDispatchServiceImpl implements SuperAgentDispatchService {

    private static final Logger log = LoggerFactory.getLogger(SuperAgentDispatchServiceImpl.class);
    private static final String SOURCE_PROVIDER_AGENTBUS = "AGENTBUS";
    private static final String CAPTURE_STATUS_RECEIVED = "RECEIVED";
    private static final String SAFE_ERROR_CODE_SUPERAGENT_FAILED = "SUPERAGENT_DISPATCH_FAILED";
    private static final String SAFE_ERROR_CODE_PAYLOAD_NOT_FOUND = "SOURCE_MESSAGE_PAYLOAD_NOT_FOUND";
    private static final String SAFE_ERROR_CODE_UNEXPECTED = "SUPERAGENT_DISPATCH_UNEXPECTED";
    private static final int SAFE_ERROR_MAX_LENGTH = 512;
    private static final Pattern SECRET_PATTERN = Pattern.compile(
            "(?i)(api[_-]?key|token|secret|password|cookie|authorization)\\s*[:=]\\s*[^\\s,;}]+");

    private final SuperAgentDispatchProperties properties;
    private final SuperAgentOpenApiProperties openApiProperties;
    private final SuperAgentDispatchRunRepository runRepository;
    private final SourceMessageInboxRepository sourceMessageRepository;
    private final SuperAgentOpenApiClient openApiClient;
    private final ObjectMapper objectMapper;

    /**
     * 注入 dispatch/outbox 所需的持久化端口、SourceMessage 端口和 SuperAgent Open API 端口。
     */
    public SuperAgentDispatchServiceImpl(
            SuperAgentDispatchProperties properties,
            SuperAgentOpenApiProperties openApiProperties,
            SuperAgentDispatchRunRepository runRepository,
            SourceMessageInboxRepository sourceMessageRepository,
            SuperAgentOpenApiClient openApiClient,
            ObjectMapper objectMapper) {
        this.properties = properties;
        this.openApiProperties = openApiProperties;
        this.runRepository = runRepository;
        this.sourceMessageRepository = sourceMessageRepository;
        this.openApiClient = openApiClient;
        this.objectMapper = objectMapper;
    }

    /**
     * AgentBus 新 SourceMessage 捕获成功后创建待分发记录；重复投递由数据库唯一键保证幂等。
     */
    @Override
    public void enqueueAgentBusRealtime(
            CaptureSourceMessageCommand command,
            SourceMessageCaptureResult captureResult) {
        if (!properties.isEnabled() || command == null || captureResult == null) {
            return;
        }
        if (captureResult.inboxId() == null
                || !CAPTURE_STATUS_RECEIVED.equals(captureResult.captureStatus())) {
            return;
        }
        if (!SOURCE_PROVIDER_AGENTBUS.equals(command.provider())) {
            return;
        }
        LocalDateTime now = nowUtc();
        Long sourceMessageId = captureResult.inboxId();
        runRepository.insertIfAbsent(new SuperAgentDispatchRunDraft(
                command.hotelId(),
                sourceMessageId,
                command.provider(),
                command.channel(),
                command.externalMessageId(),
                command.externalConversationId(),
                SuperAgentDispatchSource.AGENTBUS_REALTIME.code(),
                idempotencyKey(sourceMessageId),
                requestId(sourceMessageId),
                SuperAgentDispatchStatus.PENDING.code(),
                safeMaxAttempts(),
                now,
                now));
    }

    /**
     * 处理到期的 dispatch run。该方法可由定时 worker 或测试直接调用。
     */
    @Override
    public int processDueDispatches() {
        if (!properties.isWorkerEnabled()) {
            return 0;
        }
        LocalDateTime now = nowUtc();
        List<SuperAgentDispatchRunSnapshot> runs = runRepository.claimDue(
                "local-worker",
                List.of(
                        SuperAgentDispatchStatus.PENDING.code(),
                        SuperAgentDispatchStatus.RETRYABLE_FAILED.code(),
                        SuperAgentDispatchStatus.RUNNING.code()),
                now,
                now.plus(lockTtl()),
                safeBatchSize());
        int processed = 0;
        for (SuperAgentDispatchRunSnapshot run : runs) {
            processOne(run);
            processed++;
        }
        return processed;
    }

    /**
     * 定时处理待分发记录。开关关闭时立即返回，避免本地或测试环境误调用 SuperAgent。
     */
    @Scheduled(fixedDelayString = "${agentbus.superagent-dispatch.worker-fixed-delay-ms:10000}")
    public void scheduledProcessDueDispatches() {
        if (!properties.isWorkerEnabled()) {
            return;
        }
        try {
            processDueDispatches();
        } catch (RuntimeException exception) {
            log.warn("SuperAgent dispatch worker 运行失败，后续调度会继续重试。", exception);
        }
    }

    /**
     * 处理单条 dispatch run，成功保存结果，失败保存安全摘要和下一次尝试时间。
     */
    private void processOne(SuperAgentDispatchRunSnapshot run) {
        try {
            String payloadJson = sourceMessageRepository.findPayloadJson(run.sourceMessageId())
                    .orElseThrow(() -> new SuperAgentDispatchException(
                            SAFE_ERROR_CODE_PAYLOAD_NOT_FOUND,
                            "SourceMessage payload 不存在。"));
            SuperAgentOpenApiResult result = openApiClient.invokeMessage(new SuperAgentOpenApiMessageRequest(
                    buildMessage(payloadJson),
                    run.idempotencyKey(),
                    openApiProperties.getAgentbusExternalSubjectId(),
                    run.requestId(),
                    metadata(run)));
            runRepository.markSucceeded(new SuperAgentDispatchRunSuccessUpdate(
                    run.id(),
                    result.sessionId(),
                    result.runId(),
                    result.runUri(),
                    result.lastEventId(),
                    result.rawAnswer(),
                    parsedJson(result.rawAnswer()),
                    traceJson(result),
                    nowUtc()));
        } catch (SuperAgentDispatchException exception) {
            markFailure(run, exception.safeErrorCode(), exception.getMessage());
        } catch (SuperAgentOpenApiException exception) {
            markFailure(run, SAFE_ERROR_CODE_SUPERAGENT_FAILED, exception.getMessage());
        } catch (RuntimeException exception) {
            markFailure(run, SAFE_ERROR_CODE_UNEXPECTED, "SuperAgent dispatch 处理失败。");
        }
    }

    /**
     * 标记失败状态。尝试次数达到上限后进入 FAILED，否则进入 RETRYABLE_FAILED 等待退避重试。
     */
    private void markFailure(SuperAgentDispatchRunSnapshot run, String safeErrorCode, String safeErrorSummary) {
        LocalDateTime now = nowUtc();
        boolean exhausted = run.attemptCount() >= Math.max(1, run.maxAttempts());
        runRepository.markFailed(new SuperAgentDispatchRunFailureUpdate(
                run.id(),
                exhausted ? SuperAgentDispatchStatus.FAILED.code() : SuperAgentDispatchStatus.RETRYABLE_FAILED.code(),
                safeErrorCode,
                safeSummary(safeErrorSummary),
                exhausted ? null : now.plus(backoff(run.attemptCount())),
                now));
    }

    /**
     * 构造发给 SuperAgent 的 AgentBus 邮件处理提示，业务结果仍由后续回调或人工确认链路处理。
     */
    private String buildMessage(String payloadJson) {
        return """
                请基于以下 AgentBus 邮件 JSON 输出结构化任务抽取结果。
                只返回 SuperAgent 当前约定的 JSON 或 S10/S99 特殊结果，不要创建订单、任务、OPERA 操作或客户回复。

                AgentBus 邮件 JSON：
                """ + payloadJson;
    }

    /**
     * 构造 SuperAgent Open API metadata，便于外部系统排查 run 和 SourceMessage 的对应关系。
     */
    private Map<String, Object> metadata(SuperAgentDispatchRunSnapshot run) {
        Map<String, Object> metadata = new LinkedHashMap<>();
        metadata.put("source", "th-hotel-agentbus-realtime");
        metadata.put("dispatch_run_id", run.id());
        metadata.put("source_message_id", run.sourceMessageId());
        metadata.put("hotel_id", run.hotelId());
        metadata.put("external_message_id", run.externalMessageId());
        metadata.put("external_conversation_id", run.externalConversationId());
        metadata.put("source_provider", run.sourceProvider());
        metadata.put("source_channel", run.sourceChannel());
        return metadata;
    }

    /**
     * 尝试把 SuperAgent 最终回答压缩成 JSON 字符串。S10/S99 等纯字符串结果保留 raw answer，不写 parsed json。
     */
    private String parsedJson(String rawAnswer) {
        if (rawAnswer == null || rawAnswer.isBlank()) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(objectMapper.readTree(rawAnswer));
        } catch (JsonProcessingException exception) {
            return null;
        }
    }

    /**
     * 将公开 trace 摘要序列化保存，供后续排查 SuperAgent 运行过程。
     */
    private String traceJson(SuperAgentOpenApiResult result) {
        try {
            return objectMapper.writeValueAsString(result.traceEvents());
        } catch (JsonProcessingException exception) {
            throw new SuperAgentDispatchException(SAFE_ERROR_CODE_UNEXPECTED, "SuperAgent trace 摘要序列化失败。", exception);
        }
    }

    /**
     * 根据尝试次数计算退避时长，避免外部服务异常时频繁重试。
     */
    private Duration backoff(int attemptCount) {
        long multiplier = 1L << Math.min(Math.max(0, attemptCount - 1), 10);
        Duration candidate = initialBackoff().multipliedBy(multiplier);
        return candidate.compareTo(maxBackoff()) > 0 ? maxBackoff() : candidate;
    }

    /**
     * 生成稳定 SuperAgent 幂等键。
     */
    private String idempotencyKey(Long sourceMessageId) {
        return "agentbus-source-message-" + sourceMessageId;
    }

    /**
     * 生成稳定调用关联 ID，写入 X-Request-ID。
     */
    private String requestId(Long sourceMessageId) {
        return "agentbus-dispatch-source-" + sourceMessageId;
    }

    /**
     * 安全清理错误摘要，避免日志和数据库保存 Secret 或大段外部响应。
     */
    private String safeSummary(String value) {
        String summary = value == null || value.isBlank() ? "SuperAgent dispatch 失败。" : value;
        summary = SECRET_PATTERN.matcher(summary).replaceAll("$1:***");
        if (summary.length() > SAFE_ERROR_MAX_LENGTH) {
            summary = summary.substring(0, SAFE_ERROR_MAX_LENGTH);
        }
        return summary;
    }

    /**
     * 当前 UTC 时间，数据库 LocalDateTime 均按 UTC 解释。
     */
    private LocalDateTime nowUtc() {
        return LocalDateTime.now(ZoneOffset.UTC);
    }

    /**
     * 读取并归一化最大尝试次数。
     */
    private int safeMaxAttempts() {
        return Math.max(1, properties.getMaxAttempts());
    }

    /**
     * 读取并归一化 worker 批量大小。
     */
    private int safeBatchSize() {
        return Math.max(1, properties.getBatchSize());
    }

    /**
     * 读取并归一化 worker 锁时长。
     */
    private Duration lockTtl() {
        return properties.getLockTtl() == null || properties.getLockTtl().isNegative()
                ? Duration.ofMinutes(5)
                : properties.getLockTtl();
    }

    /**
     * 读取并归一化初始退避时长。
     */
    private Duration initialBackoff() {
        return properties.getInitialBackoff() == null || properties.getInitialBackoff().isNegative()
                ? Duration.ofSeconds(30)
                : properties.getInitialBackoff();
    }

    /**
     * 读取并归一化最大退避时长。
     */
    private Duration maxBackoff() {
        return properties.getMaxBackoff() == null || properties.getMaxBackoff().isNegative()
                ? Duration.ofMinutes(15)
                : properties.getMaxBackoff();
    }

    /**
     * dispatch 内部异常，携带可保存的安全错误代码。
     */
    private static final class SuperAgentDispatchException extends RuntimeException {
        private final String safeErrorCode;

        private SuperAgentDispatchException(String safeErrorCode, String message) {
            super(message);
            this.safeErrorCode = safeErrorCode;
        }

        private SuperAgentDispatchException(String safeErrorCode, String message, Throwable cause) {
            super(message, cause);
            this.safeErrorCode = safeErrorCode;
        }

        private String safeErrorCode() {
            return safeErrorCode;
        }
    }
}
