package com.arms.api.chat.service;

import com.arms.api.chat.model.dto.ChatDTO;
import com.arms.api.chat.model.dto.ProjectAnalysisRequestDTO;
import com.arms.api.chat.model.vo.ProjectAnalysisVO;
import com.arms.api.util.msa_communicator.EngineClient;
import com.arms.egovframework.javaservice.aigenerate.l_query.service.UserQueryService;
import com.arms.egovframework.javaservice.aigenerate.l_query.service.UserQueryServiceImpl;
import com.arms.egovframework.javaservice.aigenerate.ll_rag.model.RagResultVO;
import com.arms.egovframework.javaservice.aigenerate.lv_searchengine.model.SearchEngineDocumentVO;
import com.arms.egovframework.javaservice.aigenerate.v_prompt.model.PromptContextVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
@RequiredArgsConstructor
public class ChatServiceImpl implements UserQueryService<ChatDTO> {

    private final UserQueryServiceImpl userQueryServiceImpl;
    private final EngineClient engineClient;
    private final ChatModel chatModel;

    @Value("${prompt.project-analysis.system-message}")
    private String projectAnalysisSystemMessage;

    private final ConcurrentHashMap<String, AtomicBoolean> streamStatus = new ConcurrentHashMap<>();


    @Override
    public Flux<String> stream(ChatDTO query) {
        if (query.getPdServiceId() != null) {
            return projectAnalysisStream(query);
        }
        return userQueryServiceImpl.stream(query);
    }

    @Override
    public Mono<String> generate(ChatDTO query) {
        return userQueryServiceImpl.generate(query);
    }

    @Override
    public String stopStream(String sessionId) {
        AtomicBoolean flag = streamStatus.get(sessionId);
        if (flag != null) {
            flag.set(true);
        }
        return userQueryServiceImpl.stopStream(sessionId);
    }

    public Mono<PromptContextVO> buildContext(ChatDTO query) {
        return userQueryServiceImpl.buildContext(query);
    }

    public Flux<String> executeStream(ChatDTO query) {
        if (query.getPdServiceId() != null) {
            return projectAnalysisStream(query);
        }
        return userQueryServiceImpl.executeStream(query, query.getSessionId());
    }

    private Flux<String> projectAnalysisStream(ChatDTO query) {

        String streamId = query.getSessionId();
        streamStatus.put(streamId, new AtomicBoolean(false));

        Mono<ProjectAnalysisVO> analysisMono = Mono.fromCallable(() -> {
            log.info("ChatServiceImpl :: [1/4] Engine-Fire 프로젝트 집계 데이터 조회");
            ProjectAnalysisVO analysis = engineClient.getProjectAnalysis(
                    ProjectAnalysisRequestDTO.builder()
                            .pdServiceId(query.getPdServiceId())
                            .pdServiceVersionIds(query.getPdServiceVersionIds())
                            .build());
            logProjectAnalysis(analysis);
            return analysis;
        }).subscribeOn(Schedulers.boundedElastic());

        Mono<PromptContextVO> contextMono = userQueryServiceImpl.buildContext(query);

        return Mono.zip(analysisMono, contextMono)
                .flatMapMany(tuple -> {
                    ProjectAnalysisVO analysis = tuple.getT1();
                    PromptContextVO context = tuple.getT2();

                    log.info("ChatServiceImpl :: [2/4] 프로젝트 집계 데이터 + 프레임워크 컨텍스트 조합");

                    String systemPrompt = resolveProjectAnalysisPrompt(analysis);
                    String userPrompt = buildUserPrompt(context);

                    log.info("ChatServiceImpl :: [3/4] ChatClient 스트리밍 시작 | streamId={}", streamId);

                    return ChatClient.builder(chatModel).build()
                            .prompt()
                            .system(systemPrompt)
                            .user(userPrompt)
                            .stream()
                            .content()
                            .takeUntil(chunk -> streamStatus.getOrDefault(streamId, new AtomicBoolean(false)).get())
                            .doOnComplete(() -> {
                                log.info("ChatServiceImpl :: [4/4] 프로젝트 분석 스트리밍 완료 | streamId={}", streamId);
                                streamStatus.remove(streamId);
                            })
                            .doOnError(e -> {
                                log.error("ChatServiceImpl :: 프로젝트 분석 스트리밍 오류 | streamId={} | error={}",
                                        streamId, e.getMessage(), e);
                                streamStatus.remove(streamId);
                            });
                });
    }

    private String resolveProjectAnalysisPrompt(ProjectAnalysisVO dto) {
        return projectAnalysisSystemMessage
                .replace("{pdServiceName}", dto.getPdServiceName())
                .replace("{versionNames}", String.join(", ", dto.getVersionNames()))
                .replace("{total}", dto.getTotal().toString())
                .replace("{completed}", dto.getCompleted().toString())
                .replace("{inProgress}", dto.getInProgress().toString())
                .replace("{open}", dto.getOpen().toString())
                .replace("{completionRate}", dto.getCompletionRate().toString())
                .replace("{reqTotal}", dto.getReqTotal().toString())
                .replace("{reqCompleted}", dto.getReqCompleted().toString())
                .replace("{reqInProgress}", dto.getReqInProgress().toString())
                .replace("{reqOpen}", dto.getReqOpen().toString())
                .replace("{issueTotal}", dto.getIssueTotal().toString())
                .replace("{issueCompleted}", dto.getIssueCompleted().toString())
                .replace("{issueInProgress}", dto.getIssueInProgress().toString())
                .replace("{issueOpen}", dto.getIssueOpen().toString())
                .replace("{scopeStatus}", dto.getScopeStatus())
                .replace("{timeStatus}", dto.getTimeStatus())
                .replace("{resourceStatus}", dto.getResourceStatus())
                .replace("{overallStatus}", dto.getOverallStatus())
                .replace("{priorityDistribution}", dto.getPriorityDistribution().toString())
                .replace("{difficultyDistribution}", dto.getDifficultyDistribution().toString());
    }

    private String buildUserPrompt(PromptContextVO context) {

        StringBuilder sb = new StringBuilder();

        sb.append("[사용자 질의]\n")
          .append(context.getQueryText())
          .append("\n\n");

        List<RagResultVO> ragResults = context.getRagResults();
        if (ragResults != null && !ragResults.isEmpty()) {
            sb.append("[RAG 유사 문서]\n");
            ragResults.stream()
                    .filter(r -> r.getContent() != null && !r.getContent().isBlank())
                    .forEach(r -> sb.append("- ").append(r.getContent().trim()).append("\n"));
            sb.append("\n");
        }

        List<String> keywords = context.getKeywords();
        if (keywords != null && !keywords.isEmpty()) {
            sb.append("[핵심 키워드]\n")
              .append(String.join(", ", keywords))
              .append("\n\n");
        }

        List<SearchEngineDocumentVO> searchDocs = context.getSearchDocuments();
        if (searchDocs != null && !searchDocs.isEmpty()) {
            sb.append("[검색 문서]\n");
            searchDocs.stream()
                    .filter(d -> d.getContent() != null && !d.getContent().isBlank())
                    .forEach(d -> {
                        if (d.getTitle() != null && !d.getTitle().isBlank()) {
                            sb.append("▶ ").append(d.getTitle().trim()).append("\n");
                        }
                        sb.append(d.getContent().trim()).append("\n\n");
                    });
        }

        return sb.toString();
    }

    private void logProjectAnalysis(ProjectAnalysisVO analysis) {
        String versions = analysis.getVersionNames() == null ? "" : String.join(", ", analysis.getVersionNames());
        log.info("\n"
                + "========================================\n"
                + "📋 프로젝트 현황 데이터\n"
                + "----------------------------------------\n"
                + "■ 프로젝트 정보    제품: {} | 버전: {}\n"
                + "■ 전체 현황        전체 {}건 | 완료 {}건 | 진행중 {}건 | 열림 {}건 (완료율 {}%)\n"
                + "■ 요구사항         전체 {}건 | 완료 {}건 | 진행중 {}건 | 열림 {}건\n"
                + "■ 이슈             전체 {}건 | 완료 {}건 | 진행중 {}건 | 열림 {}건\n"
                + "■ 우선순위 분포    {}\n"
                + "■ 난이도 분포      {}\n"
                + "========================================",
                analysis.getPdServiceName(), versions,
                analysis.getTotal(), analysis.getCompleted(), analysis.getInProgress(), analysis.getOpen(), analysis.getCompletionRate(),
                analysis.getReqTotal(), analysis.getReqCompleted(), analysis.getReqInProgress(), analysis.getReqOpen(),
                analysis.getIssueTotal(), analysis.getIssueCompleted(), analysis.getIssueInProgress(), analysis.getIssueOpen(),
                analysis.getPriorityDistribution(),
                analysis.getDifficultyDistribution());
    }

}
