package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.model.entity.AiModel;
import com.agent.management.repository.AiModelRepository;
import com.agent.management.service.AiModelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

@Slf4j
@Service
public class AiModelServiceImpl implements AiModelService {

    private final AiModelRepository modelRepo;

    /** application.yml 中配置的 ChatClient（系统内部优先使用） */
    private final ChatClient ymlChatClient;

    /** ChatClient 缓存：modelId -> ChatClient */
    private final ConcurrentHashMap<Long, ChatClient> clientCache = new ConcurrentHashMap<>();

    /** 单模型调用 HTTP 超时：连接 10s / 读取 60s（底层默认无读超时，LLM 不响应时会无限挂起） */
    private static final int CONNECT_TIMEOUT_MS = 10_000;
    private static final int READ_TIMEOUT_MS = 60_000;

    public AiModelServiceImpl(AiModelRepository modelRepo,
                              @Value("${spring.ai.openai.base-url:}") String ymlBaseUrl,
                              ChatClient.Builder chatClientBuilder) {
        this.modelRepo = modelRepo;
        // 如果 yml 配置了模型，预先构建一个 ChatClient 供系统内部使用
        if (ymlBaseUrl != null && !ymlBaseUrl.isBlank()) {
            this.ymlChatClient = chatClientBuilder.build();
            log.info("[AiModel] 检测到 application.yml 模型配置: {}", ymlBaseUrl);
        } else {
            this.ymlChatClient = null;
            log.info("[AiModel] 未检测到 application.yml 模型配置，系统内部调用将使用模型管理中的模型");
        }
    }

    @Override
    public List<AiModel> listModels() {
        return modelRepo.findAllByOrderByIsDefaultDescPriorityAscCreatedAtAsc();
    }

    @Override
    public AiModel getModel(Long id) {
        return modelRepo.findById(id)
                .orElseThrow(() -> new BusinessException("模型不存在"));
    }

    @Override
    public AiModel createModel(AiModel model) {
        validateModel(model);
        if (Boolean.TRUE.equals(model.getIsDefault())) {
            clearOtherDefaults(null);
            model.setPriority(0);
        } else if (model.getPriority() == null || model.getPriority() == 0) {
            // 非默认模型且 priority 未设置或为 0，自动排在末尾
            List<AiModel> all = modelRepo.findAll();
            int maxPriority = all.stream()
                    .mapToInt(m -> m.getPriority() != null ? m.getPriority() : 0)
                    .max().orElse(0);
            model.setPriority(maxPriority + 1);
        }
        model.onCreate();
        return modelRepo.save(model);
    }

    @Override
    public AiModel updateModel(Long id, AiModel model) {
        AiModel existing = getModel(id);

        existing.setName(model.getName());
        existing.setBaseUrl(model.getBaseUrl());
        existing.setCompletionsPath(model.getCompletionsPath());
        existing.setModelName(model.getModelName());
        existing.setTemperature(model.getTemperature());

        // apiKey 为空时保留原值（前端编辑时脱敏不回传）
        if (model.getApiKey() != null && !model.getApiKey().isBlank()) {
            existing.setApiKey(model.getApiKey());
        }

        if (Boolean.TRUE.equals(model.getIsDefault()) && !Boolean.TRUE.equals(existing.getIsDefault())) {
            clearOtherDefaults(id);
            existing.setIsDefault(true);
            existing.setPriority(0);
        } else {
            existing.setIsDefault(model.getIsDefault());
        }

        validateModel(existing);
        existing.onUpdate();
        AiModel saved = modelRepo.save(existing);
        invalidateCache(id);
        return saved;
    }

    @Override
    public AiModel duplicateModel(Long id) {
        AiModel src = getModel(id);
        AiModel copy = new AiModel();
        copy.setName(src.getName() + "（复制）");
        copy.setBaseUrl(src.getBaseUrl());
        copy.setCompletionsPath(src.getCompletionsPath());
        copy.setApiKey(src.getApiKey());
        copy.setModelName(src.getModelName());
        copy.setTemperature(src.getTemperature());
        // 复制品默认非默认、排在末尾（priority 由 createModel 自动分配）
        copy.setIsDefault(false);
        copy.setPriority(null);
        log.info("[AiModel] 复制模型: {} -> {}", src.getName(), copy.getName());
        return createModel(copy);
    }

    @Override
    public void deleteModel(Long id) {
        AiModel model = getModel(id);
        modelRepo.deleteById(id);
        invalidateCache(id);
        log.info("[AiModel] 删除模型: {}", model.getName());
    }

    @Override
    public void setDefault(Long id) {
        AiModel model = getModel(id);
        clearOtherDefaults(id);
        model.setIsDefault(true);
        // 默认模型优先级设为 0
        model.setPriority(0);
        model.onUpdate();
        modelRepo.save(model);
        invalidateCache(id);
        log.info("[AiModel] 设置默认模型: {}", model.getName());
    }

    @Override
    public void updatePriorities(List<Map<String, Object>> priorities) {
        for (Map<String, Object> item : priorities) {
            Long id = Long.valueOf(item.get("id").toString());
            int priority = Integer.parseInt(item.get("priority").toString());
            AiModel model = modelRepo.findById(id).orElse(null);
            if (model != null) {
                model.setPriority(priority);
                // 第一个为默认模型
                model.setIsDefault(priority == 0);
                model.onUpdate();
                modelRepo.save(model);
                invalidateCache(id);
            }
        }
        log.info("[AiModel] 更新模型优先级: {} 条", priorities.size());
    }

    @Override
    public AiModel getDefaultModel() {
        return modelRepo.findByIsDefaultTrue().orElse(null);
    }

    @Override
    public ChatClient getDefaultChatClient() {
        AiModel defaultModel = getDefaultModel();
        if (defaultModel == null) return null;
        return getOrCreateClient(defaultModel);
    }

    @Override
    public ChatClient getChatClientOrFallback(ChatClient.Builder fallbackBuilder) {
        ChatClient client = getDefaultChatClient();
        if (client != null) return client;
        return fallbackBuilder.build();
    }

    @Override
    public <T> T routeChatCall(Function<ChatClient, T> promptFn) {
        List<AiModel> models = listModels();
        if (models.isEmpty()) {
            throw new BusinessException("没有可用的 AI 模型");
        }

        Exception lastError = null;
        for (AiModel model : models) {
            long start = System.currentTimeMillis();
            try {
                ChatClient client = getOrCreateClient(model);
                T result = promptFn.apply(client);
                log.info("[Route] 模型 {} 调用成功，耗时={}ms", model.getName(), System.currentTimeMillis() - start);
                return result;
            } catch (Exception e) {
                log.warn("[Route] 模型 {} 调用失败，耗时={}ms，尝试下一个: {}",
                        model.getName(), System.currentTimeMillis() - start, e.getMessage());
                lastError = e;
            }
        }
        throw new BusinessException("所有模型均调用失败: " + (lastError != null ? lastError.getMessage() : "未知错误"));
    }

    @Override
    public <T> T systemChatCall(Function<ChatClient, T> promptFn) {
        // 优先使用 yml 配置
        if (ymlChatClient != null) {
            try {
                T result = promptFn.apply(ymlChatClient);
                log.debug("[System] yml 模型调用成功");
                return result;
            } catch (Exception e) {
                log.warn("[System] yml 模型调用失败，降级到模型管理列表: {}", e.getMessage());
            }
        }

        // 降级到模型管理中的优先级列表
        return routeChatCall(promptFn);
    }

    @Override
    public Map<String, Object> testConnection(AiModel model) {
        long start = System.currentTimeMillis();
        try {
            ChatClient client = buildChatClient(model);
            String response = client.prompt()
                    .user("Hi")
                    .call()
                    .content();
            long latency = System.currentTimeMillis() - start;
            log.info("[AiModel] 测试连接成功: model={}, latency={}ms", model.getModelName(), latency);
            return Map.of(
                    "success", true,
                    "latencyMs", latency,
                    "response", response != null ? truncate(response, 100) : ""
            );
        } catch (Exception e) {
            long latency = System.currentTimeMillis() - start;
            log.warn("[AiModel] 测试连接失败: model={}, error={}", model.getModelName(), e.getMessage());
            return Map.of(
                    "success", false,
                    "latencyMs", latency,
                    "error", extractErrorMessage(e.getMessage())
            );
        }
    }

    @Override
    public Map<String, Object> testConnection(Long modelId) {
        AiModel model = getModel(modelId);
        return testConnection(model);
    }

    @Override
    public List<String> fetchAvailableModels(AiModel model) {
        try {
            String baseUrl = model.getBaseUrl().replaceAll("/+$", "");
            String url = baseUrl + "/models";

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("Authorization", "Bearer " + model.getApiKey())
                    .timeout(Duration.ofSeconds(10))
                    .GET()
                    .build();

            HttpClient client = HttpClient.newBuilder()
                    .connectTimeout(Duration.ofSeconds(5))
                    .build();

            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() != 200) {
                throw new BusinessException("获取模型列表失败: HTTP " + response.statusCode());
            }

            return parseModelList(response.body());
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("获取模型列表失败: " + extractErrorMessage(e.getMessage()));
        }
    }

    @Override
    public ChatClient getChatClient(Long modelId) {
        AiModel model = getModel(modelId);
        return getOrCreateClient(model);
    }

    // ==================== 内部方法 ====================

    private ChatClient getOrCreateClient(AiModel model) {
        return clientCache.computeIfAbsent(model.getId(), id -> buildChatClient(model));
    }

    ChatClient buildChatClient(AiModel model) {
        String completionsPath = (model.getCompletionsPath() != null && !model.getCompletionsPath().isBlank())
                ? model.getCompletionsPath()
                : "/chat/completions";

        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(CONNECT_TIMEOUT_MS);
        requestFactory.setReadTimeout(READ_TIMEOUT_MS);

        OpenAiApi openAiApi = OpenAiApi.builder()
                .baseUrl(model.getBaseUrl())
                .apiKey(model.getApiKey())
                .completionsPath(completionsPath)
                .restClientBuilder(RestClient.builder().requestFactory(requestFactory))
                .build();

        OpenAiChatOptions options = OpenAiChatOptions.builder()
                .model(model.getModelName())
                .temperature(model.getTemperature())
                .build();

        OpenAiChatModel chatModel = OpenAiChatModel.builder()
                .openAiApi(openAiApi)
                .defaultOptions(options)
                .build();

        return ChatClient.builder(chatModel).build();
    }

    private void invalidateCache(Long modelId) {
        clientCache.remove(modelId);
    }

    private void clearOtherDefaults(Long excludeId) {
        List<AiModel> defaults = modelRepo.findAll().stream()
                .filter(m -> Boolean.TRUE.equals(m.getIsDefault()))
                .filter(m -> excludeId == null || !m.getId().equals(excludeId))
                .toList();
        for (AiModel m : defaults) {
            m.setIsDefault(false);
            m.onUpdate();
            clientCache.remove(m.getId());
        }
        modelRepo.saveAll(defaults);
    }

    private void validateModel(AiModel model) {
        if (model.getName() == null || model.getName().isBlank()) {
            throw new BusinessException("模型名称不能为空");
        }
        if (model.getBaseUrl() == null || model.getBaseUrl().isBlank()) {
            throw new BusinessException("接口地址不能为空");
        }
        if (model.getApiKey() == null || model.getApiKey().isBlank()) {
            throw new BusinessException("API Key 不能为空");
        }
        if (model.getModelName() == null || model.getModelName().isBlank()) {
            throw new BusinessException("模型标识不能为空");
        }
    }

    private List<String> parseModelList(String json) {
        List<String> models = new ArrayList<>();
        int dataIdx = json.indexOf("\"data\"");
        if (dataIdx < 0) return models;

        int arrayStart = json.indexOf('[', dataIdx);
        int arrayEnd = json.indexOf(']', arrayStart);
        if (arrayStart < 0 || arrayEnd < 0) return models;

        String arrayContent = json.substring(arrayStart + 1, arrayEnd);
        String[] objects = arrayContent.split("\\},\\{");
        for (String obj : objects) {
            int idIdx = obj.indexOf("\"id\"");
            if (idIdx < 0) continue;
            int colonIdx = obj.indexOf(':', idIdx + 3);
            if (colonIdx < 0) continue;
            int valueStart = obj.indexOf('"', colonIdx + 1);
            if (valueStart < 0) continue;
            int valueEnd = obj.indexOf('"', valueStart + 1);
            if (valueEnd < 0) continue;
            models.add(obj.substring(valueStart + 1, valueEnd));
        }
        return models;
    }

    private String extractErrorMessage(String msg) {
        if (msg == null) return "未知错误";
        if (msg.length() > 200) return msg.substring(0, 200) + "...";
        return msg;
    }

    private String truncate(String text, int maxLen) {
        if (text == null) return "";
        return text.length() <= maxLen ? text : text.substring(0, maxLen) + "...";
    }
}
