package com.dne.ems.service.impl;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

import com.dne.ems.dto.ai.VolcanoChatRequest;
import com.dne.ems.dto.ai.VolcanoChatResponse;
import com.dne.ems.model.Feedback;
import com.dne.ems.model.enums.FeedbackStatus;
import com.dne.ems.repository.FeedbackRepository;
import com.dne.ems.service.AiReviewService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * AI反馈内容审核服务实现类
 * 
 * <p>该服务负责调用火山引擎AI接口对用户提交的反馈内容进行审核，
 * 判断内容是否合规（不含敏感词）以及是否与大气污染相关。
 * 根据AI审核结果，自动更新反馈状态。</p>
 *
 * @version 1.0
 * @since 2025-06
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class AiReviewServiceImpl implements AiReviewService {

    private final FeedbackRepository feedbackRepository;
    private final WebClient webClient;
    private final ObjectMapper objectMapper; // For parsing JSON strings

    /**
     * 火山引擎API的URL地址
     */
    @Value("${volcano.api.url}")
    private String apiUrl;

    /**
     * 火山引擎API的访问密钥
     */
    @Value("${volcano.api.key}")
    private String apiKey;

    /**
     * 使用的AI模型名称
     */
    @Value("${volcano.model.name}")
    private String modelName;

    /**
     * 应用基础URL，用于构建附件访问地址
     */
    @Value("${app.base.url}")
    private String appBaseUrl;

    /**
     * 对反馈内容进行AI审核
     * 
     * <p>该方法将反馈内容及其附件图片发送给火山引擎AI进行审核，
     * 判断内容是否合规以及是否与大气污染相关。根据AI的判断结果，
     * 自动更新反馈的状态。</p>
     *
     * @param feedback 需要审核的反馈对象
     */
    @Override
    public void reviewFeedback(Feedback feedback) {
        log.info("Starting AI review for feedback ID: {}", feedback.getId());

        List<String> imageUrls = feedback.getAttachments().stream()
                .map(attachment -> appBaseUrl + "/api/files/view/" + attachment.getStoredFileName())
                .toList();

        VolcanoChatRequest request = buildRequest(feedback.getDescription(), imageUrls);

        webClient.post()
                .uri(apiUrl)
                .header("Authorization", "Bearer " + apiKey)
                .body(Mono.just(request), VolcanoChatRequest.class)
                .retrieve()
                .bodyToMono(VolcanoChatResponse.class)
                .doOnSuccess(response -> processResponse(response, feedback.getId()))
                .onErrorResume(error -> {
                    log.error("AI API call failed for feedback ID: {}. Setting status to AI_REVIEW_FAILED.", feedback.getId(), error);
                    feedbackRepository.findById(feedback.getId()).ifPresent(freshFeedback -> {
                        freshFeedback.setStatus(FeedbackStatus.AI_REVIEW_FAILED);
                        feedbackRepository.save(freshFeedback);
                    });
                    return Mono.empty(); // Terminate the chain gracefully
                })
                .subscribe(); // Subscribe to trigger the execution
    }
    
    /**
     * 构建火山引擎AI聊天请求
     * 
     * <p>根据反馈描述和图片URL列表，构建发送给火山引擎AI的请求对象。
     * 包括系统提示、用户内容和函数定义等组件。</p>
     *
     * @param feedbackDescription 反馈文本描述
     * @param imageUrls 反馈附带的图片URL列表
     * @return 构建好的请求对象
     */
    private VolcanoChatRequest buildRequest(String feedbackDescription, List<String> imageUrls) {
        VolcanoChatRequest.Message systemMessage = VolcanoChatRequest.Message.builder()
                .role("system")
                .content(List.of(
                        VolcanoChatRequest.TextContentPart.builder().text("你是一个内容审核员。你的任务是判断用户反馈是否合规（不含敏感词）并且是否与大气污染相关。反馈可能包含文本描述和图片，图片是可选的辅助信息。你必须主要根据文本内容进行判断，即使没有图片也要完成分析。你必须使用提供的'feedback_analysis'工具来返回你的分析结果。").build()
                ))
                .build();
        
        List<VolcanoChatRequest.ContentPart> userContentParts = new java.util.ArrayList<>();
        userContentParts.add(VolcanoChatRequest.TextContentPart.builder().text(feedbackDescription).build());
        
        if (imageUrls != null) {
            for (String imageUrl : imageUrls) {
                userContentParts.add(VolcanoChatRequest.ImageUrlContentPart.builder()
                    .imageUrl(VolcanoChatRequest.ImageUrl.builder().url(imageUrl).build())
                    .build());
            }
        }

        VolcanoChatRequest.Message userMessage = VolcanoChatRequest.Message.builder()
                .role("user")
                .content(userContentParts)
                .build();

        VolcanoChatRequest.FunctionDef functionDef = VolcanoChatRequest.FunctionDef.builder()
                .name("feedback_analysis")
                .description("分析反馈内容并返回审核结果")
                .parameters(VolcanoChatRequest.Parameters.builder()
                        .type("object")
                        .properties(Map.of(
                                "is_compliant", Map.of("type", "boolean", "description", "内容是否合规"),
                                "is_relevant", Map.of("type", "boolean", "description", "内容是否与大气污染相关")
                        ))
                        .required(List.of("is_compliant", "is_relevant"))
                        .build())
                .build();
        
        VolcanoChatRequest.Tool tool = VolcanoChatRequest.Tool.builder()
                .type("function")
                .function(functionDef)
                .build();

        return VolcanoChatRequest.builder()
                .model(modelName)
                .messages(List.of(systemMessage, userMessage))
                .tools(List.of(tool))
                .build();
    }

    /**
     * 处理AI响应结果
     * 
     * <p>解析AI返回的响应，提取工具调用结果，并根据内容合规性和相关性
     * 更新反馈状态。如果内容合规且相关，状态更新为待审核；否则标记为无效关闭。
     * 处理过程中的任何异常都会导致反馈状态被设置为AI审核失败。</p>
     *
     * @param response AI返回的响应对象
     * @param feedbackId 反馈ID，用于查询和更新反馈状态
     */
    private void processResponse(VolcanoChatResponse response, Long feedbackId) {
        feedbackRepository.findById(feedbackId).ifPresentOrElse(feedback -> {
            try {
                if (response != null && response.getChoices() != null && !response.getChoices().isEmpty()) {
                    VolcanoChatResponse.ToolCall toolCall = response.getChoices().get(0).getMessage().getToolCalls().get(0);
                    
                    if ("feedback_analysis".equals(toolCall.getFunction().getName())) {
                        String argumentsJson = toolCall.getFunction().getArguments();
                        Map<String, Boolean> args = objectMapper.readValue(argumentsJson, new TypeReference<Map<String, Boolean>>() {});
                        
                        boolean isCompliant = args.getOrDefault("is_compliant", false);
                        boolean isRelevant = args.getOrDefault("is_relevant", false);

                        if (isCompliant && isRelevant) {
                            feedback.setStatus(FeedbackStatus.PENDING_REVIEW);
                            log.info("AI review PASSED for feedback ID: {}. Status set to PENDING_REVIEW.", feedback.getId());
                        } else {
                            feedback.setStatus(FeedbackStatus.CLOSED_INVALID);
                            log.warn("AI review FAILED for feedback ID: {}. Compliant: {}, Relevant: {}. Status set to CLOSED_INVALID.", feedback.getId(), isCompliant, isRelevant);
                        }
                    } else {
                         log.error("AI response tool_call function name mismatch for feedback ID: {}", feedback.getId());
                        feedback.setStatus(FeedbackStatus.AI_REVIEW_FAILED);
                    }
                } else {
                    log.error("Received empty or invalid response from AI API for feedback ID: {}", feedback.getId());
                    feedback.setStatus(FeedbackStatus.AI_REVIEW_FAILED);
                }
            } catch (JsonProcessingException | NullPointerException | IndexOutOfBoundsException e) {
                log.error("Failed to parse or process AI response for feedback ID: {}. Setting status to AI_REVIEW_FAILED.", feedback.getId(), e);
                feedback.setStatus(FeedbackStatus.AI_REVIEW_FAILED);
            } finally {
                feedbackRepository.save(feedback);
            }
        }, () -> log.error("Feedback with ID {} not found for AI review processing.", feedbackId));
    }
}