package gc.mda.kcg.domain.event;

import gc.mda.kcg.audit.annotation.Auditable;
import gc.mda.kcg.auth.AuthPrincipal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.OffsetDateTime;
import java.util.*;

/**
 * 이벤트 조회/상태 관리 서비스.
 * 모든 상태 변경은 EventWorkflow에 이력 기록.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class EventService {

    private static final Set<String> RESOLVED_STATUSES = Set.of("RESOLVED", "FALSE_POSITIVE");

    private final PredictionEventRepository eventRepository;
    private final EventWorkflowRepository workflowRepository;

    /**
     * 이벤트 목록 조회 (필터 조합).
     */
    @Transactional(readOnly = true)
    public Page<PredictionEvent> getEvents(String status, String level, String category, String vesselMmsi, Pageable pageable) {
        Specification<PredictionEvent> spec = Specification.where(null);

        if (status != null && !status.isBlank()) {
            spec = spec.and((root, query, cb) -> cb.equal(root.get("status"), status));
        }
        if (level != null && !level.isBlank()) {
            spec = spec.and((root, query, cb) -> cb.equal(root.get("level"), level));
        }
        if (category != null && !category.isBlank()) {
            spec = spec.and((root, query, cb) -> cb.equal(root.get("category"), category));
        }
        if (vesselMmsi != null && !vesselMmsi.isBlank()) {
            spec = spec.and((root, query, cb) -> cb.equal(root.get("vesselMmsi"), vesselMmsi));
        }

        // 기본 정렬: occurredAt DESC
        return eventRepository.findAll(spec, pageable);
    }

    /**
     * 이벤트 상세 조회.
     */
    @Transactional(readOnly = true)
    public PredictionEvent getEventById(Long id) {
        return eventRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("EVENT_NOT_FOUND: " + id));
    }

    /**
     * 이벤트 확인 처리 (NEW → ACK).
     */
    @Auditable(action = "ACK_EVENT", resourceType = "PREDICTION_EVENT")
    @Transactional
    public PredictionEvent acknowledgeEvent(Long id, UUID actorId, String actorName) {
        PredictionEvent event = getEventById(id);
        String prevStatus = event.getStatus();

        if (!"NEW".equals(prevStatus)) {
            throw new IllegalStateException("ACK_ONLY_FROM_NEW: current=" + prevStatus);
        }

        event.setStatus("ACK");
        event.setAssigneeId(actorId);
        event.setAssigneeName(actorName);
        event.setAckedAt(OffsetDateTime.now());

        PredictionEvent saved = eventRepository.save(event);

        workflowRepository.save(EventWorkflow.builder()
                .eventId(id)
                .prevStatus(prevStatus)
                .newStatus("ACK")
                .actorId(actorId)
                .actorName(actorName)
                .build());

        return saved;
    }

    /**
     * 이벤트 상태 변경 (범용) + EventWorkflow INSERT.
     */
    @Auditable(action = "UPDATE_EVENT_STATUS", resourceType = "PREDICTION_EVENT")
    @Transactional
    public PredictionEvent updateEventStatus(Long id, String newStatus, UUID actorId, String actorName, String comment) {
        PredictionEvent event = getEventById(id);
        String prevStatus = event.getStatus();

        event.setStatus(newStatus);

        // ACK 전환 시 acked_at 자동 설정
        if ("ACK".equals(newStatus) && event.getAckedAt() == null) {
            event.setAckedAt(OffsetDateTime.now());
            event.setAssigneeId(actorId);
            event.setAssigneeName(actorName);
        }

        // RESOLVED/FALSE_POSITIVE 전환 시 resolved_at 자동 설정
        if (RESOLVED_STATUSES.contains(newStatus) && event.getResolvedAt() == null) {
            event.setResolvedAt(OffsetDateTime.now());
        }

        if (comment != null && !comment.isBlank()) {
            event.setResolutionNote(comment);
        }

        PredictionEvent saved = eventRepository.save(event);

        workflowRepository.save(EventWorkflow.builder()
                .eventId(id)
                .prevStatus(prevStatus)
                .newStatus(newStatus)
                .actorId(actorId)
                .actorName(actorName)
                .comment(comment)
                .build());

        return saved;
    }

    /**
     * 이벤트 처리 이력 조회.
     */
    @Transactional(readOnly = true)
    public List<EventWorkflow> getEventWorkflowHistory(Long eventId) {
        return workflowRepository.findByEventIdOrderByCreatedAtDesc(eventId);
    }

    /**
     * 상태별 이벤트 카운트.
     */
    @Transactional(readOnly = true)
    public Map<String, Long> getEventStats() {
        Map<String, Long> stats = new LinkedHashMap<>();
        for (String s : List.of("NEW", "ACK", "IN_PROGRESS", "RESOLVED", "FALSE_POSITIVE", "DISMISSED")) {
            stats.put(s, eventRepository.countByStatus(s));
        }
        return stats;
    }

    // ========================================================================
    // 헬퍼
    // ========================================================================

    AuthPrincipal currentPrincipal() {
        var auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.getPrincipal() instanceof AuthPrincipal p) return p;
        return null;
    }
}
