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

import com.unicorn.hgzero.common.dto.ApiResponse;
import com.unicorn.hgzero.meeting.biz.domain.Todo;
import com.unicorn.hgzero.meeting.biz.dto.TodoDTO;
import com.unicorn.hgzero.meeting.biz.service.TodoService;
import com.unicorn.hgzero.meeting.biz.usecase.in.todo.CreateTodoUseCase;
import com.unicorn.hgzero.meeting.biz.usecase.in.todo.UpdateTodoUseCase;
import com.unicorn.hgzero.meeting.infra.dto.request.CreateTodoRequest;
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateTodoRequest;
import com.unicorn.hgzero.meeting.infra.dto.response.TodoListResponse;
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;

/**
 * Todo 관리 API Controller
 * Todo 생성, 수정, 완료 처리 기능
 */
@RestController
@RequestMapping("/api/meetings/todos")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "Todo", description = "Todo 관리 API")
public class TodoController {

    private final TodoService todoService;
    private final CacheService cacheService;
    private final EventPublisher eventPublisher;

    /**
     * Todo 생성 (할당)
     * POST /api/todos
     */
    @PostMapping
    @Operation(summary = "Todo 생성", description = "새로운 Todo를 생성하고 담당자에게 할당합니다")
    @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<TodoListResponse.TodoItem>> createTodo(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Valid @RequestBody CreateTodoRequest request) {

        log.info("Todo 생성 요청 - userId: {}, minutesId: {}, title: {}, assigneeId: {}", 
                userId, request.getMinutesId(), request.getTitle(), request.getAssigneeId());

        try {
            // Todo 생성
            Todo createdTodo = todoService.createTodo(
                    new CreateTodoUseCase.CreateTodoCommand(
                            request.getMinutesId(),
                            null, // meetingId는 나중에 회의록에서 가져올 예정
                            request.getTitle(),
                            request.getDescription(),
                            request.getAssigneeId(),
                            request.getDueDate(),
                            request.getPriority()
                    )
            );

            // 응답 DTO 생성
            TodoDTO todoDTO = TodoDTO.from(createdTodo);
            TodoListResponse.TodoItem response = convertToTodoItem(todoDTO);

            // 캐시 무효화
            cacheService.evictCacheTodoList(request.getAssigneeId());
            cacheService.evictCacheMinutesDetail(request.getMinutesId());
            cacheService.evictCacheDashboard(request.getAssigneeId());

            // Todo 할당 이벤트 발행
            eventPublisher.publishTodoAssigned(
                    createdTodo.getTodoId(),
                    createdTodo.getTitle(),
                    request.getAssigneeId(),
                    request.getAssigneeName(),
                    userId,
                    userName,
                    request.getDueDate()
            );

            log.info("Todo 생성 성공 - todoId: {}", createdTodo.getTodoId());
            return ResponseEntity.ok(ApiResponse.success(response));

        } catch (Exception e) {
            log.error("Todo 생성 실패 - minutesId: {}, title: {}", 
                    request.getMinutesId(), request.getTitle(), e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("Todo 생성에 실패했습니다"));
        }
    }

    /**
     * Todo 수정
     * PATCH /api/todos/{todoId}
     */
    @PatchMapping("/{todoId}")
    @Operation(summary = "Todo 수정", description = "Todo 정보를 수정합니다")
    public ResponseEntity<ApiResponse<TodoListResponse.TodoItem>> updateTodo(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "Todo ID") @PathVariable String todoId,
            @Valid @RequestBody UpdateTodoRequest request) {

        log.info("Todo 수정 요청 - userId: {}, todoId: {}", userId, todoId);

        try {
            // Todo 수정
            Todo updatedTodo = todoService.updateTodo(
                    new UpdateTodoUseCase.UpdateTodoCommand(
                            todoId,
                            request.getTitle(),
                            request.getDescription(),
                            request.getAssigneeId(),
                            request.getDueDate(),
                            request.getPriority()
                    )
            );

            // 응답 DTO 생성
            TodoDTO todoDTO = TodoDTO.from(updatedTodo);
            TodoListResponse.TodoItem response = convertToTodoItem(todoDTO);

            // 캐시 무효화
            cacheService.evictCacheTodoDetail(todoId);
            if (request.getAssigneeId() != null) {
                cacheService.evictCacheTodoList(request.getAssigneeId());
                cacheService.evictCacheDashboard(request.getAssigneeId());
            }
            cacheService.evictCacheMinutesDetail(updatedTodo.getMinutesId());

            log.info("Todo 수정 성공 - todoId: {}", todoId);
            return ResponseEntity.ok(ApiResponse.success(response));

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

    /**
     * Todo 완료 처리
     * PATCH /api/todos/{todoId}/complete
     */
    @PatchMapping("/{todoId}/complete")
    @Operation(summary = "Todo 완료", description = "Todo를 완료 상태로 변경합니다")
    public ResponseEntity<ApiResponse<TodoListResponse.TodoItem>> completeTodo(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "Todo ID") @PathVariable String todoId) {

        log.info("Todo 완료 요청 - userId: {}, todoId: {}", userId, todoId);

        try {
            // Todo 완료 처리
            Todo completedTodo = todoService.completeTodo(todoId);

            // 응답 DTO 생성
            TodoDTO todoDTO = TodoDTO.from(completedTodo);
            TodoListResponse.TodoItem response = convertToTodoItem(todoDTO);

            // 캐시 무효화
            cacheService.evictCacheTodoDetail(todoId);
            cacheService.evictCacheTodoList(todoDTO.getAssigneeId());
            cacheService.evictCacheMinutesDetail(todoDTO.getMinutesId());
            cacheService.evictCacheDashboard(todoDTO.getAssigneeId());

            // Todo 완료 이벤트 발행
            eventPublisher.publishTodoCompleted(
                    todoDTO.getTodoId(),
                    todoDTO.getTitle(),
                    todoDTO.getAssigneeId(),
                    todoDTO.getAssigneeName(),
                    userId,
                    userName
            );

            log.info("Todo 완료 성공 - todoId: {}", todoId);
            return ResponseEntity.ok(ApiResponse.success(response));

        } catch (Exception e) {
            log.error("Todo 완료 실패 - todoId: {}", todoId, e);
            return ResponseEntity.badRequest()
                    .body(ApiResponse.errorWithType("Todo 완료 처리에 실패했습니다"));
        }
    }

    /**
     * Todo 목록 조회
     * GET /api/todos
     */
    @GetMapping
    @Operation(summary = "Todo 목록 조회", description = "사용자의 Todo 목록을 조회합니다")
    public ResponseEntity<ApiResponse<TodoListResponse>> getTodoList(
            @RequestHeader("X-User-Id") String userId,
            @RequestHeader("X-User-Name") String userName,
            @Parameter(description = "담당자 ID (미지정 시 요청자 ID)") @RequestParam(required = false) String assigneeId,
            @Parameter(description = "Todo 상태 (PENDING, IN_PROGRESS, COMPLETED)") @RequestParam(required = false) String status,
            @Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
            @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "20") int size,
            @Parameter(description = "정렬 기준 (createdAt, dueDate, priority)") @RequestParam(defaultValue = "dueDate") String sortBy,
            @Parameter(description = "정렬 방향 (asc, desc)") @RequestParam(defaultValue = "asc") String sortDir) {

        // 담당자 ID가 지정되지 않은 경우 요청자 ID 사용
        String targetAssigneeId = (assigneeId != null) ? assigneeId : userId;
        
        log.info("Todo 목록 조회 요청 - userId: {}, assigneeId: {}, status: {}, page: {}, size: {}", 
                userId, targetAssigneeId, status, page, size);

        try {
            // 캐시 확인
            String cacheKey = String.format("todos:list:%s:%s:%d:%d:%s:%s", 
                    targetAssigneeId, status, page, size, sortBy, sortDir);
            TodoListResponse cachedResponse = cacheService.getCachedTodoList(cacheKey);
            if (cachedResponse != null) {
                log.debug("캐시된 Todo 목록 반환 - assigneeId: {}", targetAssigneeId);
                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));

            // Todo 목록 조회
            var todoPage = todoService.getTodoListByAssigneeId(targetAssigneeId, status, pageable);
            
            // 응답 DTO 생성
            List<TodoListResponse.TodoItem> todoItems = todoPage.getContent().stream()
                    .map(this::convertToTodoItem)
                    .collect(Collectors.toList());

            TodoListResponse response = TodoListResponse.builder()
                    .todoList(todoItems)
                    .totalCount(todoPage.getTotalElements())
                    .currentPage(page)
                    .totalPages(todoPage.getTotalPages())
                    .build();

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

            log.info("Todo 목록 조회 성공 - assigneeId: {}, count: {}", targetAssigneeId, todoItems.size());
            return ResponseEntity.ok(ApiResponse.success(response));

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

    // Helper methods
    private TodoListResponse.TodoItem convertToTodoItem(TodoDTO todoDTO) {
        return TodoListResponse.TodoItem.builder()
                .todoId(todoDTO.getTodoId())
                .title(todoDTO.getTitle())
                .description(todoDTO.getDescription())
                .assigneeId(todoDTO.getAssigneeId())
                .assigneeName(todoDTO.getAssigneeName())
                .priority(todoDTO.getPriority())
                .status(todoDTO.getStatus())
                .dueDate(todoDTO.getDueDate() != null ? todoDTO.getDueDate().atStartOfDay() : null)
                .createdAt(todoDTO.getCreatedAt())
                .completedAt(todoDTO.getCompletedAt())
                .completedBy(todoDTO.getCompletedBy())
                .minutesId(todoDTO.getMinutesId())
                .minutesTitle(todoDTO.getMinutesTitle())
                .meetingId(todoDTO.getMeetingId())
                .meetingTitle(todoDTO.getMeetingTitle())
                .build();
    }
}