package com.unicorn.hgzero.ai.biz.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unicorn.hgzero.ai.biz.domain.ProcessedTranscript;
import com.unicorn.hgzero.ai.biz.gateway.LlmGateway;
import com.unicorn.hgzero.ai.biz.gateway.SearchGateway;
import com.unicorn.hgzero.ai.biz.gateway.TranscriptGateway;
import com.unicorn.hgzero.ai.biz.usecase.TranscriptProcessUseCase;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 회의록 자동 작성 Service
 * LLM 기반 회의록 생성 및 저장
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TranscriptProcessService implements TranscriptProcessUseCase {

    private final LlmGateway llmGateway;
    private final SearchGateway searchGateway;
    private final TranscriptGateway transcriptGateway;
    private final ObjectMapper objectMapper;

    @Override
    @Transactional
    public ProcessedTranscript processTranscript(
        String meetingId,
        String transcriptText,
        String userId,
        String userName,
        String title,
        List<String> participants,
        List<String> agenda
    ) {
        log.info("Processing transcript for meeting: meetingId={}, userId={}", meetingId, userId);

        // 1. LLM을 통한 회의록 자동 생성
        String llmResponse = llmGateway.generateTranscript(transcriptText, title, participants, agenda);
        log.debug("LLM response received: length={}", llmResponse.length());

        // 2. LLM 응답 파싱
        ProcessedTranscript processedTranscript = parseTranscriptFromLlm(llmResponse, meetingId);

        // 3. 회의록 저장
        ProcessedTranscript saved = transcriptGateway.save(processedTranscript);
        log.info("Transcript saved: transcriptId={}, meetingId={}", saved.getTranscriptId(), meetingId);

        // 4. RAG 인덱싱 (비동기 처리 고려)
        indexTranscriptForSearch(saved);

        return saved;
    }

    @Override
    @Transactional(readOnly = true)
    public ProcessedTranscript getTranscript(String transcriptId) {
        log.debug("Retrieving transcript: transcriptId={}", transcriptId);
        return transcriptGateway.findById(transcriptId)
                .orElseThrow(() -> new IllegalArgumentException("Transcript not found: " + transcriptId));
    }

    @Override
    @Transactional(readOnly = true)
    public ProcessedTranscript getTranscriptByMeetingId(String meetingId) {
        log.debug("Retrieving transcript by meetingId: {}", meetingId);
        return transcriptGateway.findByMeetingId(meetingId)
                .orElseThrow(() -> new IllegalArgumentException("Transcript not found for meeting: " + meetingId));
    }

    /**
     * LLM 응답을 ProcessedTranscript 도메인으로 파싱
     */
    private ProcessedTranscript parseTranscriptFromLlm(String llmResponse, String meetingId) {
        try {
            JsonNode root = objectMapper.readTree(llmResponse);

            // Discussions 파싱
            List<ProcessedTranscript.DiscussionItem> discussions = new ArrayList<>();
            if (root.has("discussions")) {
                root.get("discussions").forEach(node -> {
                    discussions.add(ProcessedTranscript.DiscussionItem.builder()
                            .topic(node.get("topic").asText())
                            .speaker(node.get("speaker").asText())
                            .content(node.get("content").asText())
                            .build());
                });
            }

            // Decisions 파싱
            List<ProcessedTranscript.DecisionItem> decisions = new ArrayList<>();
            if (root.has("decisions")) {
                root.get("decisions").forEach(node -> {
                    decisions.add(ProcessedTranscript.DecisionItem.builder()
                            .content(node.get("content").asText())
                            .decisionMaker(node.get("decisionMaker").asText())
                            .category(node.get("category").asText())
                            .build());
                });
            }

            // Pending items 파싱
            List<String> pendingItems = new ArrayList<>();
            if (root.has("pendingItems")) {
                root.get("pendingItems").forEach(node -> pendingItems.add(node.asText()));
            }

            return ProcessedTranscript.builder()
                    .transcriptId(UUID.randomUUID().toString())
                    .meetingId(meetingId)
                    .summary(root.has("summary") ? root.get("summary").asText() : "")
                    .discussions(discussions)
                    .decisions(decisions)
                    .pendingItems(pendingItems)
                    .createdAt(LocalDateTime.now())
                    .status("DRAFT")
                    .build();

        } catch (JsonProcessingException e) {
            log.error("Failed to parse LLM response: {}", llmResponse, e);
            throw new RuntimeException("Failed to parse transcript from LLM", e);
        }
    }

    /**
     * RAG 검색을 위한 회의록 인덱싱
     */
    private void indexTranscriptForSearch(ProcessedTranscript transcript) {
        try {
            String content = buildSearchableContent(transcript);
            String metadata = buildMetadata(transcript);

            searchGateway.indexTranscript(transcript.getTranscriptId(), content, metadata);
            log.debug("Transcript indexed for search: transcriptId={}", transcript.getTranscriptId());

        } catch (Exception e) {
            log.error("Failed to index transcript for search: transcriptId={}",
                transcript.getTranscriptId(), e);
            // 인덱싱 실패는 치명적이지 않으므로 예외를 전파하지 않음
        }
    }

    private String buildSearchableContent(ProcessedTranscript transcript) {
        StringBuilder content = new StringBuilder();
        content.append(transcript.getSummary()).append("\n\n");

        if (transcript.getDiscussions() != null) {
            transcript.getDiscussions().forEach(d ->
                content.append(d.getTopic()).append(": ").append(d.getContent()).append("\n")
            );
        }

        if (transcript.getDecisions() != null) {
            transcript.getDecisions().forEach(d ->
                content.append("결정: ").append(d.getContent()).append("\n")
            );
        }

        return content.toString();
    }

    private String buildMetadata(ProcessedTranscript transcript) {
        try {
            return objectMapper.writeValueAsString(transcript);
        } catch (JsonProcessingException e) {
            log.warn("Failed to serialize transcript metadata", e);
            return "{}";
        }
    }
}
