package tech.easyflow.agent.runtime;

import com.easyagents.agent.runtime.agentscope.AgentHttpTransportProvider;
import com.easyagents.agent.runtime.agentscope.AgentScopeMessageAdapter;
import com.easyagents.agent.runtime.agentscope.AgentScopeModelFactory;
import com.easyagents.agent.runtime.message.AgentContentBlock;
import com.easyagents.agent.runtime.message.AgentMediaBlock;
import com.easyagents.agent.runtime.message.AgentMessage;
import com.easyagents.agent.runtime.message.AgentMessageRole;
import com.easyagents.agent.runtime.message.AgentTextBlock;
import com.easyagents.agent.runtime.model.AgentGenerationOptions;
import com.easyagents.agent.runtime.model.AgentModelFactory;
import com.easyagents.agent.runtime.model.AgentModelProviderType;
import com.easyagents.agent.runtime.model.AgentModelSpec;
import io.agentscope.core.message.ContentBlock;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.model.ChatResponse;
import io.agentscope.core.model.ExecutionConfig;
import io.agentscope.core.model.GenerateOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.service.support.VlmVerificationImage;
import tech.easyflow.ai.service.verification.ChatModelConnectivityVerifier;
import tech.easyflow.ai.service.verification.ChatModelVerificationResult;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;

/**
 * 使用 AgentScope 真实运行链路验证 Chat Model 与 VLM 连通性。
 */
@Component
public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnectivityVerifier {

    private static final Logger LOG = LoggerFactory.getLogger(AgentScopeChatModelConnectivityVerifier.class);
    /** 为未识别关闭思考扩展参数的推理模型保留足够的小额输出预算。 */
    private static final int MAX_TOKENS = 256;
    private static final Duration DEFAULT_PHASE_TIMEOUT = Duration.ofSeconds(60);

    /** AgentScope 模型工厂。 */
    private final AgentModelFactory<io.agentscope.core.model.Model> modelFactory;
    /** EasyAgents 与 AgentScope 的消息适配器。 */
    private final AgentScopeMessageAdapter messageAdapter;
    /** 单阶段最大等待时间。 */
    private final Duration phaseTimeout;

    /**
     * 使用生产运行时组件创建验证器。
     */
    public AgentScopeChatModelConnectivityVerifier() {
        this(new AgentScopeModelFactory(), new AgentScopeMessageAdapter(), DEFAULT_PHASE_TIMEOUT);
    }

    /**
     * 使用指定依赖创建验证器，供测试和受控运行环境使用。
     *
     * @param modelFactory AgentScope 模型工厂
     * @param messageAdapter 消息适配器
     * @param phaseTimeout 单阶段最大等待时间
     */
    AgentScopeChatModelConnectivityVerifier(
            AgentModelFactory<io.agentscope.core.model.Model> modelFactory,
            AgentScopeMessageAdapter messageAdapter,
            Duration phaseTimeout) {
        this.modelFactory = modelFactory;
        this.messageAdapter = messageAdapter;
        this.phaseTimeout = phaseTimeout;
    }

    /**
     * 依次验证非流式基础连接与流式响应能力。
     *
     * @param model 已补齐供应商默认配置的模型
     * @return 双阶段验证结果
     * @throws BusinessException 非流式基础连接失败时抛出
     */
    @Override
    public ChatModelVerificationResult verify(Model model) {
        AgentModelSpec modelSpec = AgentModelSpecMapper.fromModel(model);
        String effectiveHttpVersion = AgentHttpTransportProvider.resolveEffectivePolicy(
                modelSpec.getHttpVersionPolicy(), modelSpec.getBaseUrl()).name();
        AgentMessage verificationMessage = buildVerificationMessage(modelSpec.isSupportImage());

        try {
            verifyPhase(modelSpec, verificationMessage, false);
        } catch (BusinessException exception) {
            LOG.error("AgentScope model base connectivity verification failed, modelId={}, httpPolicy={}",
                    model.getId(), effectiveHttpVersion, exception);
            throw exception;
        } catch (Exception exception) {
            LOG.error("AgentScope model base connectivity verification failed, modelId={}, httpPolicy={}",
                    model.getId(), effectiveHttpVersion, exception);
            throw new BusinessException(400, 1, "模型基础连接验证失败，请查看后端日志", exception);
        }

        try {
            verifyPhase(modelSpec, verificationMessage, true);
            return ChatModelVerificationResult.passed(effectiveHttpVersion);
        } catch (Exception exception) {
            LOG.warn("AgentScope model streaming verification failed, modelId={}, httpPolicy={}",
                    model.getId(), effectiveHttpVersion, exception);
            return ChatModelVerificationResult.streamingUnavailable(effectiveHttpVersion);
        }
    }

    /**
     * 执行一次指定流式模式的模型请求并校验响应。
     *
     * @param modelSpec 运行时模型声明
     * @param verificationMessage 验证消息
     * @param stream 是否启用流式响应
     * @throws BusinessException 响应为空或 VLM 图片识别错误时抛出
     */
    private void verifyPhase(AgentModelSpec modelSpec,
                             AgentMessage verificationMessage,
                             boolean stream) {
        AgentGenerationOptions generationOptions = new AgentGenerationOptions();
        generationOptions.setStream(stream);
        generationOptions.setThinkingEnabled(false);
        disableOpenAiCompatibleThinking(modelSpec, generationOptions);
        generationOptions.setMaxTokens(MAX_TOKENS);
        io.agentscope.core.model.Model agentScopeModel = modelFactory.create(modelSpec, generationOptions);

        Msg message = messageAdapter.toMsg(verificationMessage);
        GenerateOptions requestOptions = GenerateOptions.builder()
                .stream(stream)
                .maxTokens(MAX_TOKENS)
                .executionConfig(ExecutionConfig.builder()
                        .timeout(phaseTimeout)
                        .maxAttempts(1)
                        .build())
                .build();
        List<ChatResponse> responses = agentScopeModel
                .stream(List.of(message), List.of(), requestOptions)
                .timeout(phaseTimeout)
                .collectList()
                .block(phaseTimeout.plusSeconds(1));
        String responseText = aggregateText(responses);
        validateResponse(modelSpec.isSupportImage(), responseText);
    }

    /**
     * 为支持该扩展字段的 OpenAI-compatible 服务显式关闭思考。
     *
     * <p>AgentScope 的通用 OpenAI Formatter 不会读取中立的
     * {@code thinkingEnabled} 字段，需通过扩展请求体传递；原生 OpenAI、
     * DeepSeek 等服务不接收该非标准字段，因此仅对已知兼容入口设置。</p>
     *
     * @param modelSpec 运行时模型声明
     * @param generationOptions 验证生成参数
     */
    private void disableOpenAiCompatibleThinking(
            AgentModelSpec modelSpec,
            AgentGenerationOptions generationOptions) {
        AgentModelProviderType providerType = modelSpec.getProviderType();
        if (providerType == AgentModelProviderType.OPENAI_COMPATIBLE
                || providerType == AgentModelProviderType.CUSTOM
                || providerType == AgentModelProviderType.SILICONFLOW) {
            generationOptions.getAdditionalBodyParams().put("enable_thinking", false);
        }
        Object sourceProviderType = modelSpec.getMetadata().get("sourceProviderType");
        if (sourceProviderType != null
                && "gpustack".equalsIgnoreCase(String.valueOf(sourceProviderType))) {
            // GPUStack 托管的 vLLM 模型通过 chat_template_kwargs 控制 Qwen 思考模式。
            generationOptions.getAdditionalBodyParams().put(
                    "chat_template_kwargs",
                    Map.of("enable_thinking", false));
        }
    }

    /**
     * 创建文字模型或 VLM 的最小验证消息。
     *
     * @param supportImage 是否验证图片理解能力
     * @return 验证消息
     */
    private AgentMessage buildVerificationMessage(boolean supportImage) {
        if (!supportImage) {
            return AgentMessage.text(
                    AgentMessageRole.USER,
                    "请直接回复“你好”，不要补充其他内容。");
        }
        List<AgentContentBlock> blocks = new ArrayList<>();
        blocks.add(new AgentTextBlock("请直接输出图片中的内容，不要补充其他内容。"));
        AgentMediaBlock image = new AgentMediaBlock("image");
        image.setMimeType("image/png");
        image.setData(Base64.getEncoder().encodeToString(VlmVerificationImage.pngBytes()));
        blocks.add(image);
        AgentMessage message = new AgentMessage();
        message.setRole(AgentMessageRole.USER);
        message.setContentBlocks(blocks);
        return message;
    }

    /**
     * 聚合流式响应中的全部文本增量。
     *
     * @param responses AgentScope 响应片段
     * @return 聚合后的文本
     */
    private String aggregateText(List<ChatResponse> responses) {
        StringBuilder result = new StringBuilder();
        if (responses == null) {
            return result.toString();
        }
        for (ChatResponse response : responses) {
            if (response == null || response.getContent() == null) {
                continue;
            }
            for (ContentBlock block : response.getContent()) {
                if (block instanceof TextBlock textBlock && textBlock.getText() != null) {
                    result.append(textBlock.getText());
                }
            }
        }
        return result.toString();
    }

    /**
     * 校验模型返回内容。
     *
     * @param supportImage 是否执行 VLM 图片识别校验
     * @param responseText 聚合后的响应文本
     * @throws BusinessException 响应为空或图片识别结果不匹配时抛出
     */
    private void validateResponse(boolean supportImage, String responseText) {
        if (responseText == null || responseText.isBlank()) {
            throw new BusinessException("模型未返回有效内容");
        }
        if (supportImage
                && !normalizeVerificationText(responseText)
                .contains(VlmVerificationImage.VERIFICATION_CODE)) {
            LOG.warn("VLM verification response did not contain expected code, responseSummary={}",
                    responseSummary(responseText));
            throw new BusinessException("多模态校验未通过，模型未正确识别验证图片");
        }
    }

    /**
     * 归一化 VLM 对固定验证码的常见排版输出。
     *
     * <p>模型可能将连续数字输出为 {@code 5 8 3 9} 或 {@code 5,8,3,9}，
     * 这不影响图片识别结论，因此移除非字母数字字符后再校验。</p>
     *
     * @param responseText 模型验证回复
     * @return 仅保留字母与数字的响应文本
     */
    private String normalizeVerificationText(String responseText) {
        return responseText == null
                ? ""
                : responseText.replaceAll("[^\\p{L}\\p{N}]", "");
    }

    /**
     * 生成固定验证回复的安全日志摘要。
     *
     * @param responseText 模型验证回复
     * @return 移除控制字符且最长 160 字符的摘要
     */
    private String responseSummary(String responseText) {
        String normalized = responseText == null
                ? ""
                : responseText.replaceAll("[\\p{Cntrl}]", " ").trim();
        return normalized.length() <= 160
                ? normalized
                : normalized.substring(0, 160) + "...";
    }
}
