package com.unicorn.hgzero.stt.service;

import com.unicorn.hgzero.common.exception.BusinessException;
import com.unicorn.hgzero.common.exception.ErrorCode;
import com.unicorn.hgzero.stt.domain.Recording;
import com.unicorn.hgzero.stt.dto.RecordingDto;
import com.unicorn.hgzero.stt.event.RecordingEvent;
import com.unicorn.hgzero.stt.event.publisher.EventPublisher;
import com.unicorn.hgzero.stt.repository.entity.RecordingEntity;
import com.unicorn.hgzero.stt.repository.jpa.RecordingRepository;
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.UUID;

/**
 * 녹음 서비스 구현체
 * 음성 녹음 관리 기능을 구현
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class RecordingServiceImpl implements RecordingService {
    
    private final RecordingRepository recordingRepository;
    private final EventPublisher eventPublisher;
    
    @Override
    public RecordingDto.PrepareResponse prepareRecording(RecordingDto.PrepareRequest request) {
        log.info("녹음 준비 시작 - meetingId: {}, sessionId: {}", request.getMeetingId(), request.getSessionId());
        
        // 중복 녹음 세션 체크
        if (recordingRepository.existsActiveRecordingByMeetingId(request.getMeetingId())) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE, "이미 진행 중인 녹음 세션이 있습니다");
        }
        
        // 녹음 ID 생성
        String recordingId = generateRecordingId();
        
        // 녹음 엔티티 생성
        RecordingEntity recording = RecordingEntity.builder()
                .recordingId(recordingId)
                .meetingId(request.getMeetingId())
                .sessionId(request.getSessionId())
                .status(Recording.RecordingStatus.READY)
                .language(request.getLanguage() != null ? request.getLanguage() : "ko-KR")
                .speakerCount(0)
                .segmentCount(0)
                .build();
        
        recordingRepository.save(recording);
        
        // WebSocket 스트리밍 URL 생성
        String streamUrl = String.format("wss://api.example.com/stt/v1/ws/stt/%s", request.getSessionId());
        
        log.info("녹음 준비 완료 - recordingId: {}", recordingId);
        
        return RecordingDto.PrepareResponse.builder()
                .recordingId(recordingId)
                .sessionId(request.getSessionId())
                .status("READY")
                .streamUrl(streamUrl)
                .estimatedInitTime(1100)
                .build();
    }
    
    @Override
    public RecordingDto.StatusResponse startRecording(String recordingId, RecordingDto.StartRequest request) {
        log.info("녹음 시작 - recordingId: {}, startedBy: {}", recordingId, request.getStartedBy());
        
        RecordingEntity recording = findRecordingById(recordingId);
        
        // 상태 검증
        if (recording.getStatus() != Recording.RecordingStatus.READY) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE, "녹음을 시작할 수 없는 상태입니다");
        }
        
        // 녹음 시작 처리
        recording.updateStatus(Recording.RecordingStatus.RECORDING);
        RecordingEntity savedRecording = recordingRepository.save(recording);
        
        // 녹음 시작 이벤트 발행
        RecordingEvent.RecordingStarted event = RecordingEvent.RecordingStarted.of(
                recordingId, recording.getMeetingId(), recording.getSessionId(), 
                request.getStartedBy(), recording.getLanguage()
        );
        eventPublisher.publishAsync("recording-events", event);
        
        log.info("녹음 시작 완료 - recordingId: {}", recordingId);
        
        return RecordingDto.StatusResponse.builder()
                .recordingId(recordingId)
                .status("RECORDING")
                .startTime(LocalDateTime.now())
                .duration(0)
                .build();
    }
    
    @Override
    public RecordingDto.StatusResponse stopRecording(String recordingId, RecordingDto.StopRequest request) {
        log.info("녹음 중지 - recordingId: {}, stoppedBy: {}", recordingId, request.getStoppedBy());
        
        RecordingEntity recording = findRecordingById(recordingId);
        
        // 상태 검증
        if (recording.getStatus() != Recording.RecordingStatus.RECORDING) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE, "진행 중인 녹음이 아닙니다");
        }
        
        // 녹음 중지 처리
        recording.updateStatus(Recording.RecordingStatus.STOPPED);
        LocalDateTime endTime = LocalDateTime.now();

        // 녹음 시간 계산 (임시로 30분으로 설정)
        Integer duration = 1800; // 실제로는 startTime과 endTime 차이 계산

        RecordingEntity savedRecording = recordingRepository.save(recording);

        // 녹음 중지 이벤트 발행 (음성 파일 저장하지 않으므로 파일 정보 제외)
        RecordingEvent.RecordingStopped event = RecordingEvent.RecordingStopped.of(
                recordingId, recording.getMeetingId(), request.getStoppedBy(),
                recording.getStartTime(), duration
        );
        eventPublisher.publishAsync("recording-events", event);
        
        log.info("녹음 중지 완료 - recordingId: {}, duration: {}초", recordingId, duration);
        
        return RecordingDto.StatusResponse.builder()
                .recordingId(recordingId)
                .status("STOPPED")
                .startTime(recording.getStartTime())
                .endTime(endTime)
                .duration(duration)
                .build();
    }
    
    @Override
    @Transactional(readOnly = true)
    public RecordingDto.DetailResponse getRecording(String recordingId) {
        log.debug("녹음 정보 조회 - recordingId: {}", recordingId);
        
        RecordingEntity recording = findRecordingById(recordingId);
        
        return RecordingDto.DetailResponse.builder()
                .recordingId(recording.getRecordingId())
                .meetingId(recording.getMeetingId())
                .sessionId(recording.getSessionId())
                .status(recording.getStatus().name())
                .startTime(recording.getStartTime())
                .endTime(recording.getEndTime())
                .duration(recording.getDuration())
                .speakerCount(recording.getSpeakerCount())
                .segmentCount(recording.getSegmentCount())
                .language(recording.getLanguage())
                .build();
    }
    
    /**
     * 녹음 ID로 엔티티 조회
     */
    private RecordingEntity findRecordingById(String recordingId) {
        return recordingRepository.findById(recordingId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND, "녹음을 찾을 수 없습니다"));
    }
    
    /**
     * 녹음 ID 생성
     */
    private String generateRecordingId() {
        return "REC-" + LocalDateTime.now().toString().substring(0, 10).replace("-", "") + "-" + 
               String.format("%03d", (int)(Math.random() * 1000));
    }
    
}