/*
 * @author Dongmin.lee
 * @since 2026-04-10
 * @version 26.04.10
 * @see <pre>
 *  Copyright (C) 2007 by 313 DEV GRP, Inc - All Rights Reserved
 *  Unauthorized copying of this file, via any medium is strictly prohibited
 *  Proprietary and confidential
 *  Written by 313 developer group <313@313.co.kr>, December 2010
 * </pre>
 */
package com.arms.api.sample.service;

import com.arms.api.sample.model.SampleQueryDTO;
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.v_prompt.model.PromptContextVO;
import com.arms.egovframework.javaservice.aigenerate.vl_airequest.model.AiRequestResultVO;
import com.arms.egovframework.javaservice.aigenerate.vll_airesponse.model.AiResponseRequestVO;
import com.arms.egovframework.javaservice.aigenerate.vll_airesponse.model.AiResponseResultVO;
import com.arms.egovframework.javaservice.aigenerate.vll_airesponse.model.OutputType;
import com.arms.egovframework.javaservice.aigenerate.vll_airesponse.service.AiResponseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 샘플 질의 서비스 구현체
 *
 * <p>{@link UserQueryService}&lt;{@link SampleQueryDTO}&gt; 를 구현하며,
 * {@link UserQueryServiceImpl} 에 위임하여 AI 파이프라인을 실행합니다.</p>
 *
 * <pre>
 * 파이프라인:
 *   SampleQueryDTO.queryText
 *       ↓
 *   UserQueryServiceImpl.stream() / generate()          ← RAG 직접 응답
 *   UserQueryServiceImpl.execute()                      ← RAG → Keyword → Search → LLM
 *   UserQueryServiceImpl.executeStream()                ← 위 파이프라인 스트리밍
 *       ↓
 *   AiResponseService.convert() / convertAll()          ← 파일 변환·저장
 * </pre>
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SampleQueryServiceImpl implements UserQueryService<SampleQueryDTO> {

    /** AI 파이프라인 핵심 서비스 (RAG / Keyword / SearchEngine / LLM 요청 포함) */
    private final UserQueryServiceImpl userQueryServiceImpl;

    /** AI 응답 파일 변환 서비스 (PDF / JSON / Word / PPT / Excel) */
    private final AiResponseService    aiResponseService;

    private static final int DEFAULT_KEYWORD_COUNT = 3;

    // ──────────────────────────────────────────────────────────────
    // UserQueryService 구현 (RAG 직접 응답)
    // ──────────────────────────────────────────────────────────────

    @Override
    public Flux<String> stream(SampleQueryDTO query) {
        log.info("SampleQueryServiceImpl :: stream | sessionId={}", query.getSessionId());
        return userQueryServiceImpl.stream(query);
    }

    @Override
    public Mono<String> generate(SampleQueryDTO query) {
        log.info("SampleQueryServiceImpl :: generate | sessionId={}", query.getSessionId());
        return userQueryServiceImpl.generate(query);
    }

    @Override
    public String stopStream(String sessionId) {
        log.info("SampleQueryServiceImpl :: stopStream | sessionId={}", sessionId);
        return userQueryServiceImpl.stopStream(sessionId);
    }

    // ──────────────────────────────────────────────────────────────
    // 전체 파이프라인 — 단건 응답
    // ──────────────────────────────────────────────────────────────

    /**
     * RAG → Keyword → SearchEngine → LLM 전체 파이프라인 실행 후 단건 응답을 반환합니다.
     *
     * @param query 샘플 질의 DTO
     * @return LLM 최종 응답 결과 VO
     */
    public Mono<AiRequestResultVO> execute(SampleQueryDTO query) {
        int count = resolveKeywordCount(query);
        log.info("SampleQueryServiceImpl :: execute | sessionId={}, keywordCount={}", query.getSessionId(), count);
        return userQueryServiceImpl.execute(query, count);
    }

    // ──────────────────────────────────────────────────────────────
    // 전체 파이프라인 — 스트리밍 응답
    // ──────────────────────────────────────────────────────────────

    /**
     * RAG → Keyword → SearchEngine → LLM 전체 파이프라인 실행 후 스트리밍 응답을 반환합니다.
     *
     * @param query    샘플 질의 DTO
     * @param streamId 스트림 중단에 사용할 세션 ID
     * @return 토큰 단위 스트리밍 응답
     */
    public Flux<String> executeStream(SampleQueryDTO query, String streamId) {
        int count = resolveKeywordCount(query);
        log.info("SampleQueryServiceImpl :: executeStream | sessionId={}, keywordCount={}", query.getSessionId(), count);
        return userQueryServiceImpl.executeStream(query, streamId, count);
    }

    // ──────────────────────────────────────────────────────────────
    // 파이프라인 컨텍스트 조회
    // ──────────────────────────────────────────────────────────────

    /**
     * RAG → Keyword → SearchEngine 파이프라인 결과만 반환합니다. (LLM 호출 없음)
     *
     * @param query 샘플 질의 DTO
     * @return 파이프라인 전체 컨텍스트 VO
     */
    public Mono<PromptContextVO> buildContext(SampleQueryDTO query) {
        int count = resolveKeywordCount(query);
        return userQueryServiceImpl.buildContext(query, count);
    }

    // ──────────────────────────────────────────────────────────────
    // 파일 변환 — 단일 타입
    // ──────────────────────────────────────────────────────────────

    /**
     * 전체 파이프라인 실행 후 결과를 지정 타입 파일로 저장합니다.
     *
     * @param query      샘플 질의 DTO
     * @param outputPath 저장 디렉터리 경로 (없으면 자동 생성)
     * @param outputType 출력 파일 타입
     * @param fileName   파일명 (확장자 제외, null 이면 자동 생성)
     * @return 파일 변환 결과 VO
     */
    public Mono<AiResponseResultVO> executeAndSave(SampleQueryDTO query,
                                                   String outputPath,
                                                   OutputType outputType,
                                                   String fileName) {
        return execute(query).map(aiResult ->
                aiResponseService.convert(
                        AiResponseRequestVO.builder()
                                .aiResult(aiResult)
                                .outputPath(outputPath)
                                .outputType(outputType)
                                .fileName(fileName)
                                .build()
                )
        );
    }

    // ──────────────────────────────────────────────────────────────
    // 파일 변환 — 전체 타입 (5개)
    // ──────────────────────────────────────────────────────────────

    /**
     * 전체 파이프라인 실행 후 결과를 PDF / JSON / Word / PPT / Excel 5가지 타입으로 저장합니다.
     *
     * @param query      샘플 질의 DTO
     * @param outputPath 저장 디렉터리 경로 (없으면 자동 생성)
     * @param fileName   파일명 (확장자 제외, null 이면 자동 생성)
     * @return 타입별 파일 변환 결과 목록 (5개)
     */
    public Mono<List<AiResponseResultVO>> executeAndSaveAll(SampleQueryDTO query,
                                                            String outputPath,
                                                            String fileName) {
        return execute(query).map(aiResult ->
                aiResponseService.convertAll(aiResult, outputPath, fileName)
        );
    }

    // ──────────────────────────────────────────────────────────────
    // Internal
    // ──────────────────────────────────────────────────────────────

    private int resolveKeywordCount(SampleQueryDTO query) {
        return (query.getKeywordCount() != null && query.getKeywordCount() > 0)
                ? query.getKeywordCount()
                : DEFAULT_KEYWORD_COUNT;
    }
}
