/*
 * @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.controller;

import com.arms.api.sample.model.SampleQueryDTO;
import com.arms.api.sample.service.SampleQueryServiceImpl;
import com.arms.egovframework.javaservice.aigenerate.l_query.controller.UserQueryAbstractController;
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.AiResponseResultVO;
import com.arms.egovframework.javaservice.aigenerate.vll_airesponse.model.OutputType;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 샘플 질의 컨트롤러
 *
 * <p>{@link UserQueryAbstractController} 를 확장하여 AI 파이프라인 전체를 노출합니다.</p>
 *
 * <pre>
 * 상속 엔드포인트 (UserQueryAbstractController):
 *   POST /sample/query/stream    → RAG 직접 스트리밍
 *   POST /sample/query/generate  → RAG 단건 응답
 *   GET  /sample/query/stop-stream → 스트림 중단
 *   POST /sample/query/validate  → 질의 유효성 검증
 *
 * 추가 엔드포인트 (이 컨트롤러):
 *   POST /sample/query/execute          → 전체 파이프라인 + LLM 단건 응답
 *   POST /sample/query/execute-stream   → 전체 파이프라인 + LLM 스트리밍
 *   POST /sample/query/context          → 파이프라인 컨텍스트 조회 (LLM 없음)
 *   POST /sample/query/save             → 전체 파이프라인 + 단일 타입 파일 저장
 *   POST /sample/query/save-all         → 전체 파이프라인 + 5개 타입 파일 저장
 * </pre>
 */
@Slf4j
@RestController
@RequestMapping("/sample/query")
@Tag(name = "SampleQueryController", description = "AI 파이프라인 샘플 컨트롤러")
public class SampleQueryController
        extends UserQueryAbstractController<SampleQueryServiceImpl, SampleQueryDTO> {

    @Autowired
    public SampleQueryController(SampleQueryServiceImpl service) {
        setQueryService(service);
    }

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

    /**
     * RAG → Keyword → SearchEngine → LLM 전체 파이프라인을 실행하고 단건 응답을 반환합니다.
     *
     * <p>POST /sample/query/execute</p>
     *
     * <p>요청 예시:</p>
     * <pre>{@code
     * {
     *   "queryText": "프로젝트 일정 관리 방법은?",
     *   "sessionId": "session-001",
     *   "keywordCount": 3
     * }
     * }</pre>
     */
    @Operation(summary = "[Execute] 전체 파이프라인 단건 응답",
               description = "RAG → Keyword 추출 → SearchEngine → LLM 응답을 단건으로 반환합니다.")
    @PostMapping("/execute")
    public Mono<ResponseEntity<AiRequestResultVO>> execute(@RequestBody SampleQueryDTO query) {

        log.info("SampleQueryController :: execute | sessionId={}, queryText={}",
                query.getSessionId(), query.getQueryText());

        if (!query.isValid()) {
            return Mono.just(ResponseEntity.badRequest().build());
        }

        return getQueryService().execute(query)
                .map(ResponseEntity::ok)
                .onErrorResume(e -> {
                    log.error("execute 오류 | error={}", e.getMessage(), e);
                    return Mono.just(ResponseEntity.internalServerError().build());
                });
    }

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

    /**
     * RAG → Keyword → SearchEngine → LLM 전체 파이프라인을 실행하고 스트리밍 응답을 반환합니다.
     *
     * <p>POST /sample/query/execute-stream</p>
     */
    @Operation(summary = "[Execute] 전체 파이프라인 스트리밍",
               description = "RAG → Keyword 추출 → SearchEngine → LLM 응답을 스트리밍으로 반환합니다.")
    @PostMapping(value = "/execute-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> executeStream(@RequestBody SampleQueryDTO query,
                                      @RequestParam("streamId") String streamId) {

        log.info("SampleQueryController :: executeStream | sessionId={}, streamId={}",
                query.getSessionId(), streamId);

        if (!query.isValid()) {
            return Flux.error(new IllegalArgumentException("queryText 가 비어 있습니다."));
        }

        return getQueryService().executeStream(query, streamId);
    }

    // ──────────────────────────────────────────────────────────────
    // 파이프라인 컨텍스트 조회 (LLM 호출 없음)
    // ──────────────────────────────────────────────────────────────

    /**
     * RAG → Keyword → SearchEngine 파이프라인 결과만 반환합니다. LLM 을 호출하지 않습니다.
     *
     * <p>POST /sample/query/context</p>
     */
    @Operation(summary = "[Context] 파이프라인 컨텍스트 조회",
               description = "RAG · Keyword · SearchEngine 결과를 조합한 컨텍스트를 반환합니다. LLM 호출 없음.")
    @PostMapping("/context")
    public Mono<ResponseEntity<PromptContextVO>> context(@RequestBody SampleQueryDTO query) {

        log.info("SampleQueryController :: context | sessionId={}", query.getSessionId());

        if (!query.isValid()) {
            return Mono.just(ResponseEntity.badRequest().build());
        }

        return getQueryService().buildContext(query)
                .map(ResponseEntity::ok)
                .onErrorResume(e -> {
                    log.error("context 오류 | error={}", e.getMessage(), e);
                    return Mono.just(ResponseEntity.internalServerError().build());
                });
    }

    // ──────────────────────────────────────────────────────────────
    // 파일 저장 — 단일 타입
    // ──────────────────────────────────────────────────────────────

    /**
     * 전체 파이프라인 실행 후 결과를 지정 타입의 파일로 저장합니다.
     *
     * <p>POST /sample/query/save?outputPath=C:\arms&outputType=PDF&fileName=report</p>
     *
     * <p>요청 예시:</p>
     * <pre>{@code
     * {
     *   "queryText": "요구사항 관리 방법은?",
     *   "sessionId": "session-001",
     *   "keywordCount": 3
     * }
     * }</pre>
     */
    @Operation(summary = "[Save] 파이프라인 실행 후 단일 타입 파일 저장",
               description = "전체 파이프라인 실행 후 PDF / JSON / WORD / PPT / EXCEL 중 하나로 저장합니다.")
    @PostMapping("/save")
    public Mono<ResponseEntity<AiResponseResultVO>> save(
            @RequestBody SampleQueryDTO query,
            @RequestParam("outputPath") String outputPath,
            @RequestParam("outputType") OutputType outputType,
            @RequestParam(value = "fileName", required = false) String fileName) {

        log.info("SampleQueryController :: save | sessionId={}, outputPath={}, outputType={}",
                query.getSessionId(), outputPath, outputType);

        if (!query.isValid()) {
            return Mono.just(ResponseEntity.badRequest().build());
        }

        return getQueryService().executeAndSave(query, outputPath, outputType, fileName)
                .map(result -> result.isSuccess()
                        ? ResponseEntity.ok(result)
                        : ResponseEntity.internalServerError().body(result))
                .onErrorResume(e -> {
                    log.error("save 오류 | error={}", e.getMessage(), e);
                    return Mono.just(ResponseEntity.internalServerError().build());
                });
    }

    // ──────────────────────────────────────────────────────────────
    // 파일 저장 — 전체 5개 타입
    // ──────────────────────────────────────────────────────────────

    /**
     * 전체 파이프라인 실행 후 결과를 PDF / JSON / Word / PPT / Excel 5가지 타입으로 저장합니다.
     *
     * <p>POST /sample/query/save-all?outputPath=C:\arms&fileName=report</p>
     */
    @Operation(summary = "[Save-All] 파이프라인 실행 후 전체 타입 파일 저장",
               description = "전체 파이프라인 실행 후 PDF / JSON / Word / PPT / Excel 5가지 타입으로 저장합니다.")
    @PostMapping("/save-all")
    public Mono<ResponseEntity<List<AiResponseResultVO>>> saveAll(
            @RequestBody SampleQueryDTO query,
            @RequestParam("outputPath") String outputPath,
            @RequestParam(value = "fileName", required = false) String fileName) {

        log.info("SampleQueryController :: saveAll | sessionId={}, outputPath={}",
                query.getSessionId(), outputPath);

        if (!query.isValid()) {
            return Mono.just(ResponseEntity.badRequest().build());
        }

        return getQueryService().executeAndSaveAll(query, outputPath, fileName)
                .map(ResponseEntity::ok)
                .onErrorResume(e -> {
                    log.error("saveAll 오류 | error={}", e.getMessage(), e);
                    return Mono.just(ResponseEntity.internalServerError().build());
                });
    }
}
