package com.storlead.knowledge.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.storlead.framework.common.result.Result;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;

@Component
public class HttpService {

    private final HttpClient httpClient;
    private final ObjectMapper objectMapper = JacksonHolder.OBJECT_MAPPER;


    public HttpService() {
        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(5))
                .build();
    }

    /* ================= Base Request ================= */

    private HttpRequest.Builder baseRequest(String authorization) {
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .timeout(Duration.ofSeconds(10))
                .header("Content-Type", "application/json");

        if (authorization != null && !authorization.isBlank()) {
            builder.header("Authorization", authorization);
        }
        return builder;
    }

    /* ================= Response Handler ================= */

    private <T> Result<T> handleResponse(
            HttpResponse<String> response,
            Function<String, T> deserializer
    ) {
        int status = response.statusCode();
        String body = response.body();

        if (status >= 200 && status < 300) {
            try {
                return Result.result(deserializer.apply(body));
            } catch (Exception e) {
                return (Result<T>) Result.error( "json 解析失败："+e.getMessage());
            }
        }
        return (Result<T>) Result.error("HTTP " + status + ": " + body);
    }

    /* ================= GET ================= */

    public <T> Result<T> get(
            String url,
            Map<String, ?> queryParams,
            String authorization,
            TypeReference<T> typeReference
    ) {
        try {
            String fullUrl = UrlBuilder.build(url, queryParams);
            HttpRequest request = baseRequest(authorization)
                    .uri(URI.create(fullUrl))
                    .GET()
                    .build();

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

            return handleResponse(response,
                    body -> {
                        try {
                            return objectMapper.readValue(body, typeReference);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });
        } catch (Exception e) {
            return (Result<T>) Result.error(e.getMessage());
        }
    }

    /* ================= POST ================= */

    public <T> Result<T> post(
            String url,
            Map<String, ?> queryParams,
            String authorization,
            String body,
            TypeReference<T> typeReference
    ) {
        try {
            String fullUrl = UrlBuilder.build(url, queryParams);
            HttpRequest request = baseRequest(authorization)
                    .uri(URI.create(fullUrl))
                    .POST(HttpRequest.BodyPublishers.ofString(body))
                    .build();

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

            return handleResponse(response,
                    res -> {
                        try {
                            return objectMapper.readValue(res, typeReference);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });
        } catch (Exception e) {
            return (Result<T>) Result.error(e.getMessage());
        }
    }


    public void postStream(
            String url,
            String authorization,
            String body,
            SseEmitter emitter
    ) {
        postStream(url, authorization, body, emitter, null);
    }

    public void postStream(
            String url,
            String authorization,
            String body,
            SseEmitter emitter,
            java.util.function.Consumer<String> onMessageEnd
    ) {
        try {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .timeout(Duration.ofSeconds(60))
                    .header("Authorization", authorization)
                    .header("Accept", "text/event-stream")
                    .header("Content-Type", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(body))
                    .build();

            HttpResponse<InputStream> response =
                    httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
            StringBuilder fullAnswerBuilder = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(response.body(), StandardCharsets.UTF_8))) {

                String line;

                while ((line = reader.readLine()) != null) {

                    if (line.startsWith("data:")) {
                        String data = line.substring(5);

                        if (!data.contains("message_end")) {
                            try {
                                JsonNode node = JacksonHolder.OBJECT_MAPPER.readTree(data);
                                JsonNode answerNode = node.get("answer");
                                if (answerNode != null && !answerNode.isNull()) {
                                    fullAnswerBuilder.append(answerNode.asText());
                                }
                            } catch (Exception ignore) {
                                // 非 JSON 或格式异常，跳过
                            }
                            //System.out.println(data);
                            emitter.send(data);
                        }

                        if (data.contains("message_end")) {
                            // 触发消息结束回调，传递完整数据
                            ObjectNode endNode = (ObjectNode) JacksonHolder.OBJECT_MAPPER.readTree(data);
                            endNode.put("answer", fullAnswerBuilder.toString());

                            String finalData = JacksonHolder.OBJECT_MAPPER.writeValueAsString(endNode);
                            if (onMessageEnd != null) {
                                onMessageEnd.accept(finalData.replace("/",""));
                            }
                            emitter.send(finalData);
                            emitter.complete();
                            return;
                        }
                    }
                }
            }

            emitter.complete();

        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    }

    /* ================= PUT ================= */

    public <T> Result<T> put(
            String url,
            Map<String, ?> queryParams,
            String authorization,
            String body,
            TypeReference<T> typeReference
    ) {
        try {
            String fullUrl = UrlBuilder.build(url, queryParams);
            HttpRequest request = baseRequest(authorization)
                    .uri(URI.create(fullUrl))
                    .PUT(HttpRequest.BodyPublishers.ofString(body))
                    .build();

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

            return handleResponse(response,
                    res -> {
                        try {
                            return objectMapper.readValue(res, typeReference);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });
        } catch (Exception e) {
            return (Result<T>) Result.error(e.getMessage());
        }
    }

    /* ================= PATCH ================= */

    public <T> Result<T> patch(
            String url,
            Map<String, ?> queryParams,
            String authorization,
            String body,
            TypeReference<T> typeReference
    ) {
        try {
            String fullUrl = UrlBuilder.build(url, queryParams);
            HttpRequest request = baseRequest(authorization)
                    .uri(URI.create(fullUrl))
                    .method("PATCH", HttpRequest.BodyPublishers.ofString(body))
                    .build();

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

            return handleResponse(response,
                    res -> {
                        try {
                            return objectMapper.readValue(res, typeReference);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });
        } catch (Exception e) {
            return (Result<T>) Result.error(e.getMessage());
        }
    }

    /* ================= DELETE ================= */

    public <T> Result<T> delete(
            String url,
            Map<String, ?> queryParams,
            String authorization,
            TypeReference<T> typeReference
    ) {
        try {
            String fullUrl = UrlBuilder.build(url, queryParams);
            HttpRequest request = baseRequest(authorization)
                    .uri(URI.create(fullUrl))
                    .DELETE()
                    .build();

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

            return handleResponse(response,
                    res -> {
                        try {
                            return objectMapper.readValue(res, typeReference);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });
        } catch (Exception e) {
            return (Result<T>) Result.error(e.getMessage());
        }
    }

    public <T> Result<T> uploadWithJson(
            String url,
            MultipartFile file,
            String json,
            String authorization,
            TypeReference<T> typeReference
    ) {
        try {
            String boundary = UUID.randomUUID().toString();

            Path tempFile = Files.createTempFile("upload-", file.getOriginalFilename());
            file.transferTo(tempFile.toFile());

            byte[] body = buildMultipart(
                    tempFile,
                    file.getOriginalFilename(),
                    json,
                    boundary
            );

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("Authorization", authorization)
                    .header("Content-Type", "multipart/form-data; boundary=" + boundary)
                    .POST(HttpRequest.BodyPublishers.ofByteArray(body))
                    .build();

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

            Files.deleteIfExists(tempFile);

            return handleResponse(response,
                    res -> {
                        try {
                            return objectMapper.readValue(res, typeReference);
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });

        } catch (Exception e) {
            return (Result<T>) Result.error(e.getMessage());
        }
    }


    private byte[] buildMultipart(
            Path filePath,
            String fileName,
            String json,
            String boundary
    ) throws Exception {

        String CRLF = "\r\n";
        String prefix = "--" + boundary + CRLF;
        String suffix = CRLF + "--" + boundary + "--";

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // ====== JSON Part ======
        out.write(prefix.getBytes());
        out.write(("Content-Disposition: form-data; name=\"metadata\"" + CRLF).getBytes());
        out.write(("Content-Type: application/json" + CRLF + CRLF).getBytes());
        out.write(json.getBytes(StandardCharsets.UTF_8));

        // ====== File Part ======
        out.write((CRLF + prefix).getBytes());
        out.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"" + CRLF).getBytes());
        out.write(("Content-Type: application/octet-stream" + CRLF + CRLF).getBytes());
        out.write(Files.readAllBytes(filePath));

        // ====== End ======
        out.write(suffix.getBytes());

        return out.toByteArray();
    }
}
