package com.walkguide.service;

import com.walkguide.dto.request.SendNotificationRequest;
import com.walkguide.dto.response.NotificationResponse;
import com.walkguide.entity.GuardianNotification;
import com.walkguide.enums.NotificationType;
import com.walkguide.enums.PairingStatus;
import com.walkguide.exception.PairingException;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.GuardianNotificationRepository;
import com.walkguide.repository.PairingRelationRepository;
import com.walkguide.websocket.LocationBroadcaster;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.LocalDateTime;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class NotificationService {

    private final GuardianNotificationRepository notifRepository;
    private final PairingRelationRepository pairingRelationRepository;
    private final FcmService fcmService;

    // ✅ BARU: WebSocket broadcast untuk notifikasi real-time ke User
    private final LocationBroadcaster locationBroadcaster;

    @Transactional
    public NotificationResponse sendNotification(Long guardianId, SendNotificationRequest req) {
        var pairing = pairingRelationRepository
                .findByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)
                .orElseThrow(() -> new PairingException("Tidak ada user yang dipair"));

        Long userId = pairing.getUser().getId();
        NotificationType type = "VOICE_NOTE".equalsIgnoreCase(req.getNotifType())
                ? NotificationType.VOICE_NOTE : NotificationType.TEXT;

        GuardianNotification notif = GuardianNotification.builder()
                .guardianId(guardianId)
                .userId(userId)
                .notifType(type)
                .content(req.getContent())
                .voiceNoteUrl(req.getVoiceNoteUrl())
                .voiceNoteDuration(req.getVoiceNoteDuration())
                .build();
        notif = notifRepository.save(notif);

        NotificationResponse notifResponse = toResponse(notif);

        // FCM ke user (background/killed app)
        String fcmToken = pairing.getUser().getFcmToken();
        String fcmBody = type == NotificationType.TEXT
                ? req.getContent() : "Voice note from Guardian";
        fcmService.sendToToken(fcmToken,
                "Pesan dari Guardian",
                fcmBody,
                Map.of("type",         "NOTIFICATION",
                       "notifId",       String.valueOf(notif.getId()),
                       "notifType",     type.name(),
                       "voiceNoteUrl",  req.getVoiceNoteUrl() != null ? req.getVoiceNoteUrl() : ""));

        // ✅ WebSocket ke user (foreground real-time) — User subscribe /queue/notif/{userId}
        locationBroadcaster.broadcastNotification(userId, notifResponse);
        log.debug("[NOTIF] Broadcast to User={} via WebSocket | type={}", userId, type);

        return notifResponse;
    }

    public Page<NotificationResponse> getNotifications(Long userId, Pageable pageable) {
        return notifRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable)
                .map(this::toResponse);
    }

    public long getUnreadCount(Long userId) {
        return notifRepository.countByUserIdAndIsReadFalse(userId);
    }

    @Transactional
    public void markAllRead(Long userId) {
        var unread = notifRepository.findByUserIdAndIsReadFalseOrderByCreatedAtAsc(userId);
        LocalDateTime now = LocalDateTime.now();
        unread.forEach(n -> { n.setIsRead(true); n.setReadAt(now); });
        notifRepository.saveAll(unread);
    }

    @Transactional
    public void markAsRead(Long userId, Long notifId) {
        GuardianNotification notif = notifRepository.findByIdAndUserId(notifId, userId)
                .orElseThrow(() -> new com.walkguide.exception.ResourceNotFoundException(
                        "Notifikasi tidak ditemukan"));
        notif.setIsRead(true);
        notif.setReadAt(LocalDateTime.now());
        notifRepository.save(notif);
    }

    @Transactional
    public void markOneRead(Long notifId) {
        notifRepository.findById(notifId).ifPresent(n -> {
            n.setIsRead(true);
            n.setReadAt(LocalDateTime.now());
            notifRepository.save(n);
        });
    }

    private NotificationResponse toResponse(GuardianNotification n) {
        return NotificationResponse.builder()
                .id(n.getId()).notifType(n.getNotifType().name()).content(n.getContent())
                .voiceNoteUrl(n.getVoiceNoteUrl()).voiceNoteDuration(n.getVoiceNoteDuration())
                .isRead(n.getIsRead()).readAt(n.getReadAt()).createdAt(n.getCreatedAt()).build();
    }
}
