package com.unicorn.hgzero.stt.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.unicorn.hgzero.stt.dto.RecordingDto;
import com.unicorn.hgzero.stt.service.RecordingService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import java.time.LocalDateTime;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

/**
 * 녹음 컨트롤러 통합 테스트
 */
@WebMvcTest(RecordingController.class)
@DisplayName("녹음 컨트롤러 테스트")
class RecordingControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @MockBean
    private RecordingService recordingService;
    
    private RecordingDto.PrepareRequest prepareRequest;
    private RecordingDto.PrepareResponse prepareResponse;
    
    @BeforeEach
    void setUp() {
        prepareRequest = RecordingDto.PrepareRequest.builder()
                .meetingId("MEETING-001")
                .sessionId("SESSION-001")
                .language("ko-KR")
                .build();
        
        prepareResponse = RecordingDto.PrepareResponse.builder()
                .recordingId("REC-20250123-001")
                .sessionId("SESSION-001")
                .status("READY")
                .streamUrl("wss://api.example.com/stt/v1/ws/stt/SESSION-001")
                .storagePath("recordings/MEETING-001/SESSION-001.wav")
                .estimatedInitTime(1100)
                .build();
    }
    
    @Test
    @DisplayName("녹음 준비 API 성공")
    void prepareRecording_Success() throws Exception {
        // Given
        when(recordingService.prepareRecording(any(RecordingDto.PrepareRequest.class)))
                .thenReturn(prepareResponse);
        
        // When & Then
        mockMvc.perform(post("/api/v1/stt/recordings/prepare")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(prepareRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.recordingId").value("REC-20250123-001"))
                .andExpect(jsonPath("$.data.sessionId").value("SESSION-001"))
                .andExpect(jsonPath("$.data.status").value("READY"))
                .andExpect(jsonPath("$.data.streamUrl").value("wss://api.example.com/stt/v1/ws/stt/SESSION-001"))
                .andExpect(jsonPath("$.data.estimatedInitTime").value(1100));
        
        verify(recordingService).prepareRecording(any(RecordingDto.PrepareRequest.class));
    }
    
    @Test
    @DisplayName("녹음 준비 API 실패 - 유효성 검증")
    void prepareRecording_ValidationFailure() throws Exception {
        // Given
        RecordingDto.PrepareRequest invalidRequest = RecordingDto.PrepareRequest.builder()
                .meetingId("") // 빈 값
                .sessionId("SESSION-001")
                .build();
        
        // When & Then
        mockMvc.perform(post("/api/v1/stt/recordings/prepare")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(invalidRequest)))
                .andExpect(status().isBadRequest());
        
        verify(recordingService, never()).prepareRecording(any());
    }
    
    @Test
    @DisplayName("녹음 시작 API 성공")
    void startRecording_Success() throws Exception {
        // Given
        String recordingId = "REC-20250123-001";
        RecordingDto.StartRequest startRequest = RecordingDto.StartRequest.builder()
                .startedBy("user001")
                .build();
        
        RecordingDto.StatusResponse statusResponse = RecordingDto.StatusResponse.builder()
                .recordingId(recordingId)
                .status("RECORDING")
                .startTime(LocalDateTime.now())
                .duration(0)
                .build();
        
        when(recordingService.startRecording(eq(recordingId), any(RecordingDto.StartRequest.class)))
                .thenReturn(statusResponse);
        
        // When & Then
        mockMvc.perform(post("/api/v1/stt/recordings/{recordingId}/start", recordingId)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(startRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.recordingId").value(recordingId))
                .andExpect(jsonPath("$.data.status").value("RECORDING"))
                .andExpect(jsonPath("$.data.duration").value(0));
        
        verify(recordingService).startRecording(eq(recordingId), any(RecordingDto.StartRequest.class));
    }
    
    @Test
    @DisplayName("녹음 중지 API 성공")
    void stopRecording_Success() throws Exception {
        // Given
        String recordingId = "REC-20250123-001";
        RecordingDto.StopRequest stopRequest = RecordingDto.StopRequest.builder()
                .stoppedBy("user001")
                .build();
        
        RecordingDto.StatusResponse statusResponse = RecordingDto.StatusResponse.builder()
                .recordingId(recordingId)
                .status("STOPPED")
                .startTime(LocalDateTime.now().minusMinutes(30))
                .endTime(LocalDateTime.now())
                .duration(1800)
                .fileSize(172800000L)
                .storagePath("recordings/MEETING-001/SESSION-001.wav")
                .build();
        
        when(recordingService.stopRecording(eq(recordingId), any(RecordingDto.StopRequest.class)))
                .thenReturn(statusResponse);
        
        // When & Then
        mockMvc.perform(post("/api/v1/stt/recordings/{recordingId}/stop", recordingId)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(stopRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.recordingId").value(recordingId))
                .andExpect(jsonPath("$.data.status").value("STOPPED"))
                .andExpect(jsonPath("$.data.duration").value(1800))
                .andExpect(jsonPath("$.data.fileSize").value(172800000L));
        
        verify(recordingService).stopRecording(eq(recordingId), any(RecordingDto.StopRequest.class));
    }
    
    @Test
    @DisplayName("녹음 조회 API 성공")
    void getRecording_Success() throws Exception {
        // Given
        String recordingId = "REC-20250123-001";
        
        RecordingDto.DetailResponse detailResponse = RecordingDto.DetailResponse.builder()
                .recordingId(recordingId)
                .meetingId("MEETING-001")
                .sessionId("SESSION-001")
                .status("STOPPED")
                .startTime(LocalDateTime.now().minusMinutes(30))
                .endTime(LocalDateTime.now())
                .duration(1800)
                .speakerCount(3)
                .segmentCount(45)
                .storagePath("recordings/MEETING-001/SESSION-001.wav")
                .language("ko-KR")
                .build();
        
        when(recordingService.getRecording(recordingId)).thenReturn(detailResponse);
        
        // When & Then
        mockMvc.perform(get("/api/v1/stt/recordings/{recordingId}", recordingId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success").value(true))
                .andExpect(jsonPath("$.data.recordingId").value(recordingId))
                .andExpect(jsonPath("$.data.meetingId").value("MEETING-001"))
                .andExpect(jsonPath("$.data.sessionId").value("SESSION-001"))
                .andExpect(jsonPath("$.data.status").value("STOPPED"))
                .andExpect(jsonPath("$.data.duration").value(1800))
                .andExpect(jsonPath("$.data.speakerCount").value(3))
                .andExpect(jsonPath("$.data.segmentCount").value(45))
                .andExpect(jsonPath("$.data.language").value("ko-KR"));
        
        verify(recordingService).getRecording(recordingId);
    }
    
    @Test
    @DisplayName("존재하지 않는 녹음 조회 실패")
    void getRecording_NotFound() throws Exception {
        // Given
        String recordingId = "REC-NOTFOUND-001";
        
        when(recordingService.getRecording(recordingId))
                .thenThrow(new com.unicorn.hgzero.common.exception.BusinessException(
                        com.unicorn.hgzero.common.exception.ErrorCode.ENTITY_NOT_FOUND, 
                        "녹음을 찾을 수 없습니다"
                ));
        
        // When & Then
        mockMvc.perform(get("/api/v1/stt/recordings/{recordingId}", recordingId))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.success").value(false))
                .andExpect(jsonPath("$.message").value("녹음을 찾을 수 없습니다"));
        
        verify(recordingService).getRecording(recordingId);
    }
}