package gc.mda.kcg.domain.fleet;

import gc.mda.kcg.audit.annotation.Auditable;
import gc.mda.kcg.auth.AuthPrincipal;
import gc.mda.kcg.domain.fleet.dto.*;
import gc.mda.kcg.domain.fleet.repository.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
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.List;

/**
 * 모선 워크플로우 핵심 서비스 (HYBRID).
 * - 후보 데이터: iran 백엔드 API 호출 (현재 stub)
 * - 운영자 결정: 자체 DB (gear_group_parent_resolution 등)
 *
 * 모든 쓰기 액션은 @Auditable로 감사로그 자동 기록.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ParentInferenceWorkflowService {

    private final ParentResolutionRepository resolutionRepository;
    private final ParentReviewLogRepository reviewLogRepository;
    private final CandidateExclusionRepository exclusionRepository;
    private final LabelSessionRepository labelSessionRepository;

    // ========================================================================
    // Resolution (모선 확정/거부/리셋)
    // ========================================================================

    @Transactional(readOnly = true)
    public Page<ParentResolution> listReview(String status, Pageable pageable) {
        if (status == null || status.isBlank()) {
            return resolutionRepository.findAllByOrderByUpdatedAtDesc(pageable);
        }
        return resolutionRepository.findByStatusOrderByUpdatedAtDesc(status, pageable);
    }

    @Auditable(action = "REVIEW_PARENT", resourceType = "GEAR_GROUP")
    @Transactional
    public ParentResolution review(String groupKey, Integer subClusterId, ReviewRequest req) {
        AuthPrincipal principal = currentPrincipal();
        ParentResolution res = resolutionRepository
                .findByGroupKeyAndSubClusterId(groupKey, subClusterId)
                .orElseGet(() -> ParentResolution.builder()
                        .groupKey(groupKey)
                        .subClusterId(subClusterId)
                        .status("UNRESOLVED")
                        .build());

        OffsetDateTime now = OffsetDateTime.now();
        switch (req.action().toUpperCase()) {
            case "CONFIRM" -> {
                res.setStatus("MANUAL_CONFIRMED");
                res.setSelectedParentMmsi(req.selectedParentMmsi());
                res.setApprovedBy(principal != null ? principal.getUserId() : null);
                res.setApprovedAt(now);
                res.setManualComment(req.comment());
            }
            case "REJECT" -> {
                res.setStatus("REVIEW_REQUIRED");
                res.setRejectedCandidateMmsi(req.selectedParentMmsi());
                res.setRejectedAt(now);
                res.setManualComment(req.comment());
            }
            case "RESET" -> {
                res.setStatus("UNRESOLVED");
                res.setSelectedParentMmsi(null);
                res.setRejectedCandidateMmsi(null);
                res.setApprovedBy(null);
                res.setApprovedAt(null);
                res.setRejectedAt(null);
                res.setManualComment(req.comment());
            }
            default -> throw new IllegalArgumentException("UNKNOWN_ACTION: " + req.action());
        }

        ParentResolution saved = resolutionRepository.save(res);

        reviewLogRepository.save(ParentReviewLog.builder()
                .groupKey(groupKey)
                .subClusterId(subClusterId)
                .action(req.action().toUpperCase())
                .selectedParentMmsi(req.selectedParentMmsi())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .comment(req.comment())
                .build());

        return saved;
    }

    // ========================================================================
    // Exclusion (후보 제외)
    // ========================================================================

    @Auditable(action = "EXCLUDE_CANDIDATE_GROUP", resourceType = "GEAR_GROUP")
    @Transactional
    public CandidateExclusion excludeForGroup(String groupKey, Integer subClusterId, ExclusionRequest req) {
        AuthPrincipal principal = currentPrincipal();
        CandidateExclusion exc = CandidateExclusion.builder()
                .scopeType("GROUP")
                .groupKey(groupKey)
                .subClusterId(subClusterId)
                .excludedMmsi(req.excludedMmsi())
                .reason(req.reason())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .build();
        CandidateExclusion saved = exclusionRepository.save(exc);

        reviewLogRepository.save(ParentReviewLog.builder()
                .groupKey(groupKey)
                .subClusterId(subClusterId)
                .action("EXCLUDE_GROUP")
                .selectedParentMmsi(req.excludedMmsi())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .comment(req.reason())
                .build());

        return saved;
    }

    @Auditable(action = "EXCLUDE_CANDIDATE_GLOBAL", resourceType = "GEAR_GROUP")
    @Transactional
    public CandidateExclusion excludeGlobal(GlobalExclusionRequest req) {
        AuthPrincipal principal = currentPrincipal();
        CandidateExclusion exc = CandidateExclusion.builder()
                .scopeType("GLOBAL")
                .excludedMmsi(req.excludedMmsi())
                .reason(req.reason())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .build();
        CandidateExclusion saved = exclusionRepository.save(exc);

        reviewLogRepository.save(ParentReviewLog.builder()
                .groupKey("__GLOBAL__")
                .action("EXCLUDE_GLOBAL")
                .selectedParentMmsi(req.excludedMmsi())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .comment(req.reason())
                .build());

        return saved;
    }

    @Auditable(action = "RELEASE_EXCLUSION", resourceType = "GEAR_GROUP")
    @Transactional
    public CandidateExclusion releaseExclusion(Long exclusionId, CancelRequest req) {
        AuthPrincipal principal = currentPrincipal();
        CandidateExclusion exc = exclusionRepository.findById(exclusionId)
                .orElseThrow(() -> new IllegalArgumentException("EXCLUSION_NOT_FOUND: " + exclusionId));
        exc.setReleasedAt(OffsetDateTime.now());
        exc.setReleasedBy(principal != null ? principal.getUserId() : null);
        exc.setReleasedByAcnt(principal != null ? principal.getUserAcnt() : null);
        CandidateExclusion saved = exclusionRepository.save(exc);

        reviewLogRepository.save(ParentReviewLog.builder()
                .groupKey(exc.getGroupKey() != null ? exc.getGroupKey() : "__GLOBAL__")
                .action("RELEASE_EXCLUSION")
                .selectedParentMmsi(exc.getExcludedMmsi())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .comment(req != null ? req.reason() : null)
                .build());

        return saved;
    }

    @Transactional(readOnly = true)
    public Page<CandidateExclusion> listExclusions(String scopeType, Pageable pageable) {
        if (scopeType == null || scopeType.isBlank()) {
            return exclusionRepository.findActive(pageable);
        }
        return exclusionRepository.findActiveByScope(scopeType, pageable);
    }

    // ========================================================================
    // Label Session (학습 세션)
    // ========================================================================

    @Auditable(action = "LABEL_PARENT_CREATE", resourceType = "GEAR_GROUP")
    @Transactional
    public LabelSession createLabelSession(String groupKey, Integer subClusterId, LabelSessionRequest req) {
        AuthPrincipal principal = currentPrincipal();
        LabelSession session = LabelSession.builder()
                .groupKey(groupKey)
                .subClusterId(subClusterId)
                .labelParentMmsi(req.labelParentMmsi())
                .anchorSnapshot(req.anchorSnapshot())
                .createdBy(principal != null ? principal.getUserId() : null)
                .createdByAcnt(principal != null ? principal.getUserAcnt() : null)
                .build();
        LabelSession saved = labelSessionRepository.save(session);

        reviewLogRepository.save(ParentReviewLog.builder()
                .groupKey(groupKey)
                .subClusterId(subClusterId)
                .action("LABEL_PARENT")
                .selectedParentMmsi(req.labelParentMmsi())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .build());

        return saved;
    }

    @Auditable(action = "LABEL_PARENT_CANCEL", resourceType = "GEAR_GROUP")
    @Transactional
    public LabelSession cancelLabelSession(Long sessionId, CancelRequest req) {
        AuthPrincipal principal = currentPrincipal();
        LabelSession session = labelSessionRepository.findById(sessionId)
                .orElseThrow(() -> new IllegalArgumentException("LABEL_SESSION_NOT_FOUND: " + sessionId));
        session.setStatus("CANCELLED");
        session.setCancelledAt(OffsetDateTime.now());
        session.setCancelledBy(principal != null ? principal.getUserId() : null);
        session.setCancelReason(req != null ? req.reason() : null);
        LabelSession saved = labelSessionRepository.save(session);

        reviewLogRepository.save(ParentReviewLog.builder()
                .groupKey(session.getGroupKey())
                .subClusterId(session.getSubClusterId())
                .action("CANCEL_LABEL")
                .selectedParentMmsi(session.getLabelParentMmsi())
                .actor(principal != null ? principal.getUserId() : null)
                .actorAcnt(principal != null ? principal.getUserAcnt() : null)
                .comment(req != null ? req.reason() : null)
                .build());

        return saved;
    }

    @Transactional(readOnly = true)
    public Page<LabelSession> listLabelSessions(String status, Pageable pageable) {
        if (status == null || status.isBlank()) {
            return labelSessionRepository.findAllByOrderByCreatedAtDesc(pageable);
        }
        return labelSessionRepository.findByStatusOrderByCreatedAtDesc(status, pageable);
    }

    // ========================================================================
    // 도메인 로그 조회
    // ========================================================================

    @Transactional(readOnly = true)
    public Page<ParentReviewLog> listReviewLogs(String groupKey, Pageable pageable) {
        if (groupKey == null || groupKey.isBlank()) {
            return reviewLogRepository.findAllByOrderByCreatedAtDesc(pageable);
        }
        return reviewLogRepository.findByGroupKeyOrderByCreatedAtDesc(groupKey, pageable);
    }

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

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