package com.emoon.tongue.service;

import cn.hutool.json.JSONUtil;
import com.emoon.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.net.http.HttpClient;

/**
 * 模型转发服务：
 * 1. 公网 jar 调用内网 jar 的模型 controller
 * 2. 内网 jar 再调用真实模型网关
 */
@Slf4j
@Service
public class modelRelayService {

    @Value("${model-relay.target-base-url:${forward.target-base-url:http://61.178.77.37:8100/service5/robot}}")
    private String targetBaseUrl;

    @Value("${model-relay.target-path:/api/v1/model-relay/internal/chat/completions}")
    private String targetPath;

    @Value("${model-relay.target-host-header:${forward.target-host-header:}}")
    private String targetHostHeader;

    @Value("${ai.llm.url:}")
    private String llmUrl;

    @Value("${ai.llm.api-key:}")
    private String llmApiKey;

    @Value("${ai.llm.model:}")
    private String defaultModel;

    @Value("${ai.llm.max-tokens:4096}")
    private Integer defaultMaxTokens;

    @Value("${ai.llm.stream:false}")
    private boolean defaultStream;

    @Value("${ai.llm.enable-thinking:false}")
    private boolean enableThinking;

    @Value("${ai.llm.insecure-ssl:false}")
    private boolean insecureSsl;

    @Value("${model-relay.connect-timeout-ms:5000}")
    private int connectTimeoutMs;

    @Value("${model-relay.read-timeout-ms:120000}")
    private int readTimeoutMs;

    public ResponseEntity<String> forwardChatCompletions(Map<String, Object> requestBody) {
        String url = joinUrl(targetBaseUrl, targetPath);
        String jsonBody = JSONUtil.toJsonStr(requestBody == null ? Map.of() : requestBody);
        log.info("模型请求开始转发到内网 controller，url={}, payloadLength={}", url, jsonBody.length());

        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON, MediaType.ALL));
            if (targetHostHeader != null && !targetHostHeader.isBlank()) {
                headers.set(HttpHeaders.HOST, targetHostHeader.trim());
            }
            HttpEntity<String> req = new HttpEntity<>(jsonBody, headers);
            ResponseEntity<String> resp;
            try {
                resp = buildRestTemplate().postForEntity(url, req, String.class);
            } catch (ResourceAccessException e) {
                log.warn("模型转发到内网 controller 失败，尝试降级重试，url={}, reason={}", url, e.getMessage());
                resp = buildSimpleRestTemplate().postForEntity(url, req, String.class);
            }
            return toJsonResponse(resp);
        } catch (Exception e) {
            log.error("模型转发到内网 controller 失败，url={}", url, e);
            return buildFailResponse(HttpStatus.BAD_GATEWAY, "转发失败：" + e.getMessage());
        }
    }

    public ResponseEntity<String> callInternalChatCompletions(Map<String, Object> requestBody) {
        if (llmUrl == null || llmUrl.trim().isEmpty()) {
            return buildFailResponse(HttpStatus.BAD_GATEWAY, "未配置 ai.llm.url，无法调用模型网关");
        }

        Map<String, Object> payload = buildPayload(requestBody);
        Object messages = payload.get("messages");
        if (messages == null) {
            return buildFailResponse(HttpStatus.BAD_REQUEST, "请求体缺少 messages 字段");
        }

        String jsonBody = JSONUtil.toJsonStr(payload);
        log.info("内网模型 controller 开始调用模型网关，url={}, payloadLength={}", llmUrl, jsonBody.length());

        try {
            return doPostJson(llmUrl, jsonBody);
        } catch (Exception e) {
            log.error("内网模型 controller 调用模型网关失败，url={}", llmUrl, e);
            return buildFailResponse(HttpStatus.BAD_GATEWAY, "模型调用失败：" + e.getMessage());
        }
    }

    private Map<String, Object> buildPayload(Map<String, Object> requestBody) {
        Map<String, Object> payload = new LinkedHashMap<>();
        if (requestBody != null) {
            payload.putAll(requestBody);
        }

        if (!payload.containsKey("model") && defaultModel != null && !defaultModel.trim().isEmpty()) {
            payload.put("model", defaultModel.trim());
        }
        if (!payload.containsKey("max_tokens") && defaultMaxTokens != null) {
            payload.put("max_tokens", defaultMaxTokens);
        }
        if (!payload.containsKey("stream")) {
            payload.put("stream", defaultStream);
        }

        Map<String, Object> chatTemplateKwargs = new LinkedHashMap<>();
        Object rawChatTemplateKwargs = payload.get("chat_template_kwargs");
        if (rawChatTemplateKwargs instanceof Map<?, ?> rawMap) {
            rawMap.forEach((key, value) -> {
                if (key != null) {
                    chatTemplateKwargs.put(String.valueOf(key), value);
                }
            });
        }
        chatTemplateKwargs.putIfAbsent("enable_thinking", enableThinking);
        payload.put("chat_template_kwargs", chatTemplateKwargs);
        return payload;
    }

    private RestTemplate buildRestTemplate() {
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofMillis(connectTimeoutMs))
            .version(HttpClient.Version.HTTP_1_1)
            .build();
        JdkClientHttpRequestFactory rf = new JdkClientHttpRequestFactory(client);
        rf.setReadTimeout(Duration.ofMillis(readTimeoutMs));
        return new RestTemplate(rf);
    }

    private RestTemplate buildSimpleRestTemplate() {
        SimpleClientHttpRequestFactory rf = new SimpleClientHttpRequestFactory();
        rf.setConnectTimeout(connectTimeoutMs);
        rf.setReadTimeout(readTimeoutMs);
        return new RestTemplate(rf);
    }

    private ResponseEntity<String> doPostJson(String url, String jsonBody) throws Exception {
        RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(connectTimeoutMs)
            .setSocketTimeout(readTimeoutMs)
            .build();

        try (CloseableHttpClient httpClient = createHttpClient(requestConfig)) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setConfig(requestConfig);
            httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            httpPost.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE + ", */*");
            if (llmApiKey != null && !llmApiKey.trim().isEmpty()) {
                httpPost.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + llmApiKey.trim());
            }
            httpPost.setEntity(new StringEntity(jsonBody, ContentType.APPLICATION_JSON));

            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                int statusCode = response.getStatusLine().getStatusCode();
                String responseBody = response.getEntity() == null
                    ? ""
                    : EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                String contentTypeValue = response.getEntity() != null && response.getEntity().getContentType() != null
                    ? response.getEntity().getContentType().getValue()
                    : MediaType.APPLICATION_JSON_VALUE;

                log.info("模型转发完成，url={}, status={}", url, statusCode);
                return ResponseEntity.status(statusCode)
                    .contentType(resolveContentType(contentTypeValue))
                    .body(responseBody);
            }
        }
    }

    private String joinUrl(String base, String path) {
        String b = base == null ? "" : base.trim();
        String p = path == null ? "" : path.trim();
        if (b.endsWith("/")) {
            b = b.substring(0, b.length() - 1);
        }
        if (!p.startsWith("/")) {
            p = "/" + p;
        }
        return b + p;
    }

    private ResponseEntity<String> toJsonResponse(ResponseEntity<String> upstream) {
        if (upstream == null) {
            return buildFailResponse(HttpStatus.BAD_GATEWAY, "转发失败：上游无响应");
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return new ResponseEntity<>(upstream.getBody(), headers, upstream.getStatusCode());
    }

    private CloseableHttpClient createHttpClient(RequestConfig requestConfig) throws Exception {
        if (!insecureSsl) {
            return HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .build();
        }

        var sslContext = SSLContextBuilder.create()
            .loadTrustMaterial(null, (chain, authType) -> true)
            .build();

        return HttpClients.custom()
            .setDefaultRequestConfig(requestConfig)
            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
            .setSSLContext(sslContext)
            .build();
    }

    private MediaType resolveContentType(String contentTypeValue) {
        try {
            return MediaType.parseMediaType(contentTypeValue);
        } catch (Exception ignored) {
            return MediaType.APPLICATION_JSON;
        }
    }

    private ResponseEntity<String> buildFailResponse(HttpStatus status, String message) {
        return ResponseEntity.status(status)
            .contentType(MediaType.APPLICATION_JSON)
            .body(JSONUtil.toJsonStr(R.fail(message)));
    }
}
