package com.unicorn.hgzero.meeting.infra.controller;

import com.unicorn.hgzero.common.dto.ApiResponse;
import com.unicorn.hgzero.meeting.biz.dto.MinutesDTO;
import com.unicorn.hgzero.meeting.biz.service.MinutesService;
import com.unicorn.hgzero.meeting.biz.service.MinutesSectionService;
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateMinutesRequest;
import com.unicorn.hgzero.meeting.infra.dto.response.MinutesDetailResponse;
import com.unicorn.hgzero.meeting.infra.dto.response.MinutesListResponse;
import com.unicorn.hgzero.meeting.infra.cache.CacheService;
import com.unicorn.hgzero.meeting.infra.event.publisher.EventPublisher;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 회의록 관리 API Controller
 * 회의록 조회, 수정, 확정, 섹션 관리 기능
 */
@RestController
@RequestMapping("/api/minutes")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "Minutes", description = "회의록 관리 API")
public class MinutesController {

    private final MinutesService minutesService;
    private final MinutesSectionService minutesSectionService;
    private final CacheService cacheService;
    private final EventPublisher eventPublisher;

    /**
     * 회의록 목록 조회
     * GET /api/minutes
     */
    @GetMapping
    @Operation(summary = "회의록 목록 조회", description = "사용자의 회의록 목록을 조회합니다")
    @ApiResponses(value = {
            @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
            @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"),
            @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "인증 실패")
    })
    public ResponseEntity<ApiResponse<MinutesListResponse>> getMinutesList(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
            @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "20") int size,
            @Parameter(description = "정렬 기준 (createdAt, lastModifiedAt)") @RequestParam(defaultValue = "lastModifiedAt") String sortBy,
            @Parameter(description = "정렬 방향 (asc, desc)") @RequestParam(defaultValue = "desc") String sortDir) {

        log.info("회의록 목록 조회 요청 - userId: {}, page: {}, size: {}", userId, page, size);

        try {
            // 캐시 확인
            String cacheKey = String.format("minutes:list:%s:%d:%d:%s:%s", userId, page, size, sortBy, sortDir);
            MinutesListResponse cachedResponse = cacheService.getCachedMinutesList(cacheKey);
            if (cachedResponse != null) {
                log.debug("캐시된 회의록 목록 반환 - userId: {}", userId);
                return ResponseEntity.ok(ApiResponse.success(cachedResponse));
            }

            // 정렬 설정
            Sort.Direction direction = sortDir.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC;
            Pageable pageable = PageRequest.of(page, size, Sort.by(direction, sortBy));

            // 회의록 목록 조회
            var minutesPage = minutesService.getMinutesListByUserId(userId, pageable);
            
            // 응답 DTO 생성
            List<MinutesListResponse.MinutesItem> minutesItems = minutesPage.getContent().stream()
                    .map(this::convertToMinutesItem)
                    .collect(Collectors.toList());

            MinutesListResponse response = MinutesListResponse.builder()
                    .minutesList(minutesItems)
                    .totalCount(minutesPage.getTotalElements())
                    .currentPage(page)
                    .totalPages(minutesPage.getTotalPages())
                    .build();

            // 캐시 저장
            cacheService.cacheMinutesList(cacheKey, response);

            log.info("회의록 목록 조회 성공 - userId: {}, count: {}", userId, minutesItems.size());
            return ResponseEntity.ok(ApiResponse.success(response));

        } catch (Exception e) {
            log.error("회의록 목록 조회 실패 - userId: {}", userId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("회의록 목록 조회에 실패했습니다"));
        }
    }

    /**
     * 회의록 상세 조회
     * GET /api/minutes/{minutesId}
     */
    @GetMapping("/{minutesId}")
    @Operation(summary = "회의록 상세 조회", description = "회의록 상세 정보를 조회합니다")
    public ResponseEntity<ApiResponse<MinutesDetailResponse>> getMinutesDetail(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "회의록 ID") @PathVariable String minutesId) {

        log.info("회의록 상세 조회 요청 - userId: {}, minutesId: {}", userId, minutesId);

        try {
            // 캐시 확인
            MinutesDetailResponse cachedResponse = cacheService.getCachedMinutesDetail(minutesId);
            if (cachedResponse != null) {
                log.debug("캐시된 회의록 상세 반환 - minutesId: {}", minutesId);
                return ResponseEntity.ok(ApiResponse.success(cachedResponse));
            }

            // 회의록 조회
            MinutesDTO minutesDTO = minutesService.getMinutesById(minutesId);
            
            // 응답 DTO 생성
            MinutesDetailResponse response = convertToMinutesDetailResponse(minutesDTO);

            // 캐시 저장
            cacheService.cacheMinutesDetail(minutesId, response);

            log.info("회의록 상세 조회 성공 - minutesId: {}", minutesId);
            return ResponseEntity.ok(ApiResponse.success(response));

        } catch (Exception e) {
            log.error("회의록 상세 조회 실패 - minutesId: {}", minutesId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("회의록 상세 조회에 실패했습니다"));
        }
    }

    /**
     * 회의록 수정
     * PATCH /api/minutes/{minutesId}
     */
    @PatchMapping("/{minutesId}")
    @Operation(summary = "회의록 수정", description = "회의록 제목과 메모를 수정합니다")
    public ResponseEntity<ApiResponse<MinutesDetailResponse>> updateMinutes(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "회의록 ID") @PathVariable String minutesId,
            @Valid @RequestBody UpdateMinutesRequest request) {

        log.info("회의록 수정 요청 - userId: {}, minutesId: {}, title: {}", 
                userId, minutesId, request.getTitle());

        try {
            // 회의록 수정
            MinutesDTO updatedMinutes = minutesService.updateMinutes(minutesId, request.getTitle(), 
                    request.getMemo(), userId);
            
            // 응답 DTO 생성
            MinutesDetailResponse response = convertToMinutesDetailResponse(updatedMinutes);

            // 캐시 무효화
            cacheService.evictCacheMinutesDetail(minutesId);
            cacheService.evictCacheMinutesList(userId);

            log.info("회의록 수정 성공 - minutesId: {}, version: {}", 
                    minutesId, updatedMinutes.getVersion());
            return ResponseEntity.ok(ApiResponse.success(response));

        } catch (Exception e) {
            log.error("회의록 수정 실패 - minutesId: {}", minutesId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("회의록 수정에 실패했습니다"));
        }
    }

    /**
     * 회의록 확정
     * POST /api/minutes/{minutesId}/finalize
     */
    @PostMapping("/{minutesId}/finalize")
    @Operation(summary = "회의록 확정", description = "회의록을 확정 상태로 변경합니다")
    public ResponseEntity<ApiResponse<MinutesDetailResponse>> finalizeMinutes(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "회의록 ID") @PathVariable String minutesId) {

        log.info("회의록 확정 요청 - userId: {}, minutesId: {}", userId, minutesId);

        try {
            // 회의록 확정
            MinutesDTO finalizedMinutes = minutesService.finalizeMinutesDTO(minutesId, userId);
            
            // 응답 DTO 생성
            MinutesDetailResponse response = convertToMinutesDetailResponse(finalizedMinutes);

            // 캐시 무효화
            cacheService.evictCacheMinutesDetail(minutesId);
            cacheService.evictCacheMinutesList(userId);

            // 회의록 확정 이벤트 발행
            eventPublisher.publishMinutesFinalized(minutesId, finalizedMinutes.getTitle(), userId, userName);

            log.info("회의록 확정 성공 - minutesId: {}", minutesId);
            return ResponseEntity.ok(ApiResponse.success(response));

        } catch (Exception e) {
            log.error("회의록 확정 실패 - minutesId: {}", minutesId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("회의록 확정에 실패했습니다"));
        }
    }

    /**
     * 섹션 검증 완료
     * POST /api/minutes/{minutesId}/sections/{sectionId}/verify
     */
    @PostMapping("/{minutesId}/sections/{sectionId}/verify")
    @Operation(summary = "섹션 검증 완료", description = "회의록 섹션 검증을 완료합니다")
    public ResponseEntity<ApiResponse<String>> verifySectionComplete(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "회의록 ID") @PathVariable String minutesId,
            @Parameter(description = "섹션 ID") @PathVariable String sectionId) {

        log.info("섹션 검증 완료 요청 - userId: {}, minutesId: {}, sectionId: {}", 
                userId, minutesId, sectionId);

        try {
            // 섹션 검증 완료
            minutesSectionService.verifySectionComplete(sectionId, userId);

            // 캐시 무효화
            cacheService.evictCacheMinutesDetail(minutesId);

            log.info("섹션 검증 완료 성공 - sectionId: {}", sectionId);
            return ResponseEntity.ok(ApiResponse.success("섹션 검증이 완료되었습니다"));

        } catch (Exception e) {
            log.error("섹션 검증 완료 실패 - sectionId: {}", sectionId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("섹션 검증 완료에 실패했습니다"));
        }
    }

    /**
     * 섹션 잠금
     * POST /api/minutes/{minutesId}/sections/{sectionId}/lock
     */
    @PostMapping("/{minutesId}/sections/{sectionId}/lock")
    @Operation(summary = "섹션 잠금", description = "회의록 섹션을 잠금합니다")
    public ResponseEntity<ApiResponse<String>> lockSection(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "회의록 ID") @PathVariable String minutesId,
            @Parameter(description = "섹션 ID") @PathVariable String sectionId) {

        log.info("섹션 잠금 요청 - userId: {}, minutesId: {}, sectionId: {}", 
                userId, minutesId, sectionId);

        try {
            // 섹션 잠금
            minutesSectionService.lockSection(sectionId, userId);

            // 캐시 무효화
            cacheService.evictCacheMinutesDetail(minutesId);

            log.info("섹션 잠금 성공 - sectionId: {}", sectionId);
            return ResponseEntity.ok(ApiResponse.success("섹션이 잠금되었습니다"));

        } catch (Exception e) {
            log.error("섹션 잠금 실패 - sectionId: {}", sectionId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("섹션 잠금에 실패했습니다"));
        }
    }

    /**
     * 섹션 잠금 해제
     * DELETE /api/minutes/{minutesId}/sections/{sectionId}/lock
     */
    @DeleteMapping("/{minutesId}/sections/{sectionId}/lock")
    @Operation(summary = "섹션 잠금 해제", description = "회의록 섹션 잠금을 해제합니다")
    public ResponseEntity<ApiResponse<String>> unlockSection(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "회의록 ID") @PathVariable String minutesId,
            @Parameter(description = "섹션 ID") @PathVariable String sectionId) {

        log.info("섹션 잠금 해제 요청 - userId: {}, minutesId: {}, sectionId: {}", 
                userId, minutesId, sectionId);

        try {
            // 섹션 잠금 해제
            minutesSectionService.unlockSection(sectionId, userId);

            // 캐시 무효화
            cacheService.evictCacheMinutesDetail(minutesId);

            log.info("섹션 잠금 해제 성공 - sectionId: {}", sectionId);
            return ResponseEntity.ok(ApiResponse.success("섹션 잠금이 해제되었습니다"));

        } catch (Exception e) {
            log.error("섹션 잠금 해제 실패 - sectionId: {}", sectionId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("섹션 잠금 해제에 실패했습니다"));
        }
    }

    // Helper methods
    private MinutesListResponse.MinutesItem convertToMinutesItem(MinutesDTO minutesDTO) {
        return MinutesListResponse.MinutesItem.builder()
                .minutesId(minutesDTO.getMinutesId())
                .title(minutesDTO.getTitle())
                .meetingTitle(minutesDTO.getMeetingTitle())
                .status(minutesDTO.getStatus())
                .version(minutesDTO.getVersion())
                .createdAt(minutesDTO.getCreatedAt())
                .lastModifiedAt(minutesDTO.getLastModifiedAt())
                .createdBy(minutesDTO.getCreatedBy())
                .lastModifiedBy(minutesDTO.getLastModifiedBy())
                .todoCount(minutesDTO.getTodoCount())
                .completedTodoCount(minutesDTO.getCompletedTodoCount())
                .build();
    }

    private MinutesDetailResponse convertToMinutesDetailResponse(MinutesDTO minutesDTO) {
        return MinutesDetailResponse.builder()
                .minutesId(minutesDTO.getMinutesId())
                .title(minutesDTO.getTitle())
                .memo(minutesDTO.getMemo())
                .status(minutesDTO.getStatus())
                .version(minutesDTO.getVersion())
                .createdAt(minutesDTO.getCreatedAt())
                .lastModifiedAt(minutesDTO.getLastModifiedAt())
                .createdBy(minutesDTO.getCreatedBy())
                .lastModifiedBy(minutesDTO.getLastModifiedBy())
                .meeting(convertToMeetingInfo(minutesDTO.getMeeting()))
                .sections(convertToSectionInfoList(minutesDTO.getSectionsInfo()))
                .todos(convertToTodoInfoList(minutesDTO.getTodos()))
                .build();
    }

    private MinutesDetailResponse.MeetingInfo convertToMeetingInfo(MinutesDTO.MeetingInfo meetingInfo) {
        if (meetingInfo == null) return null;
        
        return MinutesDetailResponse.MeetingInfo.builder()
                .meetingId(meetingInfo.getMeetingId())
                .title(meetingInfo.getTitle())
                .scheduledAt(meetingInfo.getScheduledAt())
                .startedAt(meetingInfo.getStartedAt())
                .endedAt(meetingInfo.getEndedAt())
                .organizerId(meetingInfo.getOrganizerId())
                .organizerName(meetingInfo.getOrganizerName())
                .build();
    }

    private List<MinutesDetailResponse.SectionInfo> convertToSectionInfoList(
            List<MinutesDTO.SectionInfo> sections) {
        if (sections == null) return List.of();
        
        return sections.stream()
                .map(section -> MinutesDetailResponse.SectionInfo.builder()
                        .sectionId(section.getSectionId())
                        .title(section.getTitle())
                        .content(section.getContent())
                        .orderIndex(section.getOrderIndex())
                        .isLocked(section.isLocked())
                        .isVerified(section.isVerified())
                        .lockedBy(section.getLockedBy())
                        .lockedAt(section.getLockedAt())
                        .verifiedBy(section.getVerifiedBy())
                        .verifiedAt(section.getVerifiedAt())
                        .build())
                .collect(Collectors.toList());
    }

    private List<MinutesDetailResponse.TodoInfo> convertToTodoInfoList(
            List<MinutesDTO.TodoInfo> todos) {
        if (todos == null) return List.of();
        
        return todos.stream()
                .map(todo -> MinutesDetailResponse.TodoInfo.builder()
                        .todoId(todo.getTodoId())
                        .title(todo.getTitle())
                        .description(todo.getDescription())
                        .assigneeId(todo.getAssigneeId())
                        .assigneeName(todo.getAssigneeName())
                        .priority(todo.getPriority())
                        .status(todo.getStatus())
                        .dueDate(todo.getDueDate())
                        .completedAt(todo.getCompletedAt())
                        .completedBy(todo.getCompletedBy())
                        .build())
                .collect(Collectors.toList());
    }
}