package gc.mda.kcg.domain.enforcement;

import gc.mda.kcg.domain.enforcement.dto.CreatePlanRequest;
import gc.mda.kcg.domain.enforcement.dto.CreateRecordRequest;
import gc.mda.kcg.domain.enforcement.dto.UpdateRecordRequest;
import gc.mda.kcg.domain.enforcement.repository.EnforcementPlanRepository;
import gc.mda.kcg.domain.enforcement.repository.EnforcementRecordRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class EnforcementService {

    private final EnforcementRecordRepository recordRepository;
    private final EnforcementPlanRepository planRepository;
    private final EntityManager entityManager;

    private static final DateTimeFormatter UID_DATE_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");

    // ========================================================================
    // 단속 이력
    // ========================================================================

    public Page<EnforcementRecord> listRecords(String violationType, String vesselMmsi, Pageable pageable) {
        if (vesselMmsi != null && !vesselMmsi.isBlank()) {
            return recordRepository.findByVesselMmsiOrderByEnforcedAtDesc(vesselMmsi, pageable);
        }
        if (violationType != null && !violationType.isBlank()) {
            return recordRepository.findByViolationType(violationType, pageable);
        }
        return recordRepository.findAllByOrderByEnforcedAtDesc(pageable);
    }

    public EnforcementRecord getRecord(Long id) {
        return recordRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("EnforcementRecord not found: " + id));
    }

    @Transactional
    public EnforcementRecord createRecord(CreateRecordRequest req) {
        EnforcementRecord record = EnforcementRecord.builder()
                .enfUid(generateEnfUid())
                .eventId(req.eventId())
                .enforcedAt(req.enforcedAt())
                .zoneCode(req.zoneCode())
                .areaName(req.areaName())
                .lat(req.lat())
                .lon(req.lon())
                .vesselMmsi(req.vesselMmsi())
                .vesselName(req.vesselName())
                .flagCountry(req.flagCountry())
                .violationType(req.violationType())
                .action(req.action())
                .result(req.result())
                .aiMatchStatus(req.aiMatchStatus())
                .aiConfidence(req.aiConfidence())
                .patrolShipId(req.patrolShipId())
                .enforcedBy(req.enforcedBy())
                .enforcedByName(req.enforcedByName())
                .remarks(req.remarks())
                .build();

        EnforcementRecord saved = recordRepository.save(record);

        // event_id가 있으면 prediction_events.status를 RESOLVED로 갱신
        if (req.eventId() != null) {
            entityManager.createQuery(
                    "UPDATE PredictionEvent e SET e.status = 'RESOLVED', e.resolvedAt = :now, e.updatedAt = :now WHERE e.id = :eventId"
            )
            .setParameter("now", OffsetDateTime.now())
            .setParameter("eventId", req.eventId())
            .executeUpdate();
        }

        return saved;
    }

    @Transactional
    public EnforcementRecord updateRecord(Long id, UpdateRecordRequest req) {
        EnforcementRecord record = getRecord(id);
        if (req.result() != null) record.setResult(req.result());
        if (req.aiMatchStatus() != null) record.setAiMatchStatus(req.aiMatchStatus());
        if (req.remarks() != null) record.setRemarks(req.remarks());
        return recordRepository.save(record);
    }

    // ========================================================================
    // 단속 계획
    // ========================================================================

    public Page<EnforcementPlan> listPlans(String status, Pageable pageable) {
        if (status != null && !status.isBlank()) {
            return planRepository.findByStatusOrderByPlannedDateAsc(status, pageable);
        }
        return planRepository.findAllByOrderByPlannedDateDesc(pageable);
    }

    @Transactional
    public EnforcementPlan createPlan(CreatePlanRequest req) {
        EnforcementPlan plan = EnforcementPlan.builder()
                .planUid("PLN-" + LocalDate.now().format(UID_DATE_FMT) + "-" + UUID.randomUUID().toString().substring(0, 4).toUpperCase())
                .title(req.title())
                .zoneCode(req.zoneCode())
                .areaName(req.areaName())
                .lat(req.lat())
                .lon(req.lon())
                .plannedDate(req.plannedDate())
                .plannedFrom(req.plannedFrom())
                .plannedTo(req.plannedTo())
                .riskLevel(req.riskLevel())
                .riskScore(req.riskScore())
                .assignedShipCount(req.assignedShipCount())
                .assignedCrew(req.assignedCrew())
                .alertStatus(req.alertStatus())
                .createdBy(req.createdBy())
                .remarks(req.remarks())
                .build();

        return planRepository.save(plan);
    }

    // ========================================================================
    // UID 생성: ENF-yyyyMMdd-NNNN (일 단위 시퀀스)
    // ========================================================================

    private String generateEnfUid() {
        String dateStr = LocalDate.now().format(UID_DATE_FMT);
        String prefix = "ENF-" + dateStr + "-";

        Long count = (Long) entityManager.createQuery(
                "SELECT COUNT(r) FROM EnforcementRecord r WHERE r.enfUid LIKE :prefix"
        )
        .setParameter("prefix", prefix + "%")
        .getSingleResult();

        return prefix + String.format("%04d", count + 1);
    }
}
