package com.walkguide.service;

import com.walkguide.dto.response.PairingStatusResponse;
import com.walkguide.entity.*;
import com.walkguide.enums.*;
import com.walkguide.exception.PairingException;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class PairingService {

    private final PairingRelationRepository pairingRelationRepository;
    private final UserRepository userRepository;
    private final VoiceCommandConfigRepository voiceCommandConfigRepository;
    private final HardwareShortcutRepository hardwareShortcutRepository;
    private final AiConfigRepository aiConfigRepository;
    private final ActivityLogService activityLogService;
    private final FcmService fcmService;

    @Transactional
    public PairingStatusResponse inviteUser(Long guardianId, String uniqueUserId) {
        // Guardian tidak boleh punya pairing ACTIVE atau PENDING
        if (pairingRelationRepository.existsByGuardian_IdAndStatus(guardianId, PairingStatus.ACTIVE)) {
            throw new PairingException("Kamu sudah memiliki user yang dipair. Unpair dulu sebelum invite user baru.");
        }
        if (pairingRelationRepository.existsByGuardian_IdAndStatus(guardianId, PairingStatus.PENDING)) {
            throw new PairingException("Kamu sudah punya invite yang menunggu. Tunggu user menerima atau tolak dulu.");
        }

        User guardian = userRepository.findById(guardianId)
                .orElseThrow(() -> new ResourceNotFoundException("Guardian tidak ditemukan"));
        User user = userRepository.findByUniqueUserId(uniqueUserId)
                .orElseThrow(() -> new ResourceNotFoundException("User dengan ID '" + uniqueUserId + "' tidak ditemukan"));

        if (!"ROLE_USER".equals(user.getRole())) {
            throw new PairingException("ID tersebut bukan milik User. Pastikan kamu memasukkan ID yang benar.");
        }
        if (pairingRelationRepository.existsByUser_IdAndStatus(user.getId(), PairingStatus.ACTIVE)) {
            throw new PairingException("User ini sudah dipair dengan Guardian lain.");
        }

        PairingRelation pairing = PairingRelation.builder()
                .guardian(guardian)
                .user(user)
                .status(PairingStatus.PENDING)
                .build();
        pairing = pairingRelationRepository.save(pairing);

        // Kirim FCM ke user
        fcmService.sendToToken(user.getFcmToken(),
                "Pairing Request",
                "Guardian " + guardian.getDisplayName() + " mengundang kamu untuk terhubung",
                Map.of("type", "PAIRING_INVITE", "guardianName", guardian.getDisplayName()));

        activityLogService.createLog(guardian, ActivityLogType.PAIRING_INVITE_SENT,
                "Guardian mengirim invite ke " + user.getDisplayName(), null);

        return buildStatus(pairing, guardian, user, "GUARDIAN");
    }

    @Transactional
    public PairingStatusResponse respondToPairing(Long userId, Long pairingId, boolean accept) {
        PairingRelation pairing = pairingRelationRepository.findById(pairingId)
                .orElseThrow(() -> new ResourceNotFoundException("Pairing tidak ditemukan"));

        if (!pairing.getUser().getId().equals(userId)) {
            throw new PairingException("Kamu tidak berhak merespons pairing ini");
        }
        if (pairing.getStatus() != PairingStatus.PENDING) {
            throw new PairingException("Pairing ini sudah direspons sebelumnya");
        }

        User user     = pairing.getUser();
        User guardian = pairing.getGuardian();

        pairing.setStatus(accept ? PairingStatus.ACTIVE : PairingStatus.REJECTED);
        pairing.setRespondedAt(LocalDateTime.now());
        pairingRelationRepository.save(pairing);

        if (accept) {
            seedDefaults(guardian.getId(), user.getId());
            activityLogService.createLog(user, ActivityLogType.PAIRING_ACCEPTED,
                    "User menerima pairing dengan Guardian " + guardian.getDisplayName(), null);
            fcmService.sendToToken(guardian.getFcmToken(),
                    "Pairing Berhasil!",
                    user.getDisplayName() + " menerima undangan pairing kamu",
                    Map.of("type", "PAIRING_RESPONSE", "accepted", "true"));
        } else {
            activityLogService.createLog(user, ActivityLogType.PAIRING_REJECTED,
                    "User menolak pairing dengan Guardian " + guardian.getDisplayName(), null);
            fcmService.sendToToken(guardian.getFcmToken(),
                    "Pairing Ditolak",
                    user.getDisplayName() + " menolak undangan pairing kamu",
                    Map.of("type", "PAIRING_RESPONSE", "accepted", "false"));
        }

        return buildStatus(pairing, guardian, user, "USER");
    }

    @Transactional
    public void unpair(Long requesterId) {
        var pairing = pairingRelationRepository.findByGuardian_Id(requesterId)
                .or(() -> pairingRelationRepository.findByUser_Id(requesterId))
                .orElseThrow(() -> new ResourceNotFoundException("Tidak ada pairing yang aktif"));

        User guardian = pairing.getGuardian();
        User user     = pairing.getUser();

        // Hapus semua konfigurasi yang terkait dengan pasangan ini
        voiceCommandConfigRepository.deleteByUserId(user.getId());
        hardwareShortcutRepository.deleteByUserId(user.getId());
        aiConfigRepository.findByUserId(user.getId()).ifPresent(aiConfigRepository::delete);

        pairingRelationRepository.delete(pairing);

        // Beritahu pihak lain
        boolean requesterIsGuardian = guardian.getId().equals(requesterId);
        String otherFcmToken = requesterIsGuardian ? user.getFcmToken() : guardian.getFcmToken();
        String requesterName = requesterIsGuardian ? guardian.getDisplayName() : user.getDisplayName();

        fcmService.sendToToken(otherFcmToken,
                "Pairing Diakhiri",
                requesterName + " mengakhiri koneksi pairing",
                Map.of("type", "PAIRING_DISSOLVED"));

        activityLogService.createLog(guardian, ActivityLogType.PAIRING_DISSOLVED,
                "Pairing antara " + guardian.getDisplayName() + " dan " + user.getDisplayName() + " diakhiri", null);
    }

    public PairingStatusResponse getStatus(Long userId, String role) {
        if ("ROLE_GUARDIAN".equals(role)) {
            return pairingRelationRepository.findByGuardian_Id(userId)
                    .map(p -> buildStatus(p, p.getGuardian(), p.getUser(), "GUARDIAN"))
                    .orElse(PairingStatusResponse.builder().status("NONE").build());
        } else {
            return pairingRelationRepository.findByUser_Id(userId)
                    .map(p -> buildStatus(p, p.getGuardian(), p.getUser(), "USER"))
                    .orElse(PairingStatusResponse.builder().status("NONE").build());
        }
    }

    // ========== PRIVATE ==========

    private void seedDefaults(Long guardianId, Long userId) {
        // Voice commands default
        List<VoiceCommandConfig> defaults = List.of(
            vc(guardianId, userId, VoiceCommandKey.OPEN_WALKGUIDE,    "Open Walkguide"),
            vc(guardianId, userId, VoiceCommandKey.START_WALKGUIDE,   "Start Walkguide"),
            vc(guardianId, userId, VoiceCommandKey.STOP_WALKGUIDE,    "Stop Walkguide"),
            vc(guardianId, userId, VoiceCommandKey.CALL_GUARDIAN,     "Call Guardian"),
            vc(guardianId, userId, VoiceCommandKey.OPEN_NOTIFICATION, "Open Notifications"),
            vc(guardianId, userId, VoiceCommandKey.READ_ALL_NOTIF,    "Read All My Notifications"),
            vc(guardianId, userId, VoiceCommandKey.OPEN_SOS,         "Open SOS"),
            vc(guardianId, userId, VoiceCommandKey.SEND_SOS,         "Send SOS"),
            vc(guardianId, userId, VoiceCommandKey.WHERE_AM_I,       "Where Am I"),
            vc(guardianId, userId, VoiceCommandKey.OPEN_ACTIVITY,    "Open Activity Log"),
            vc(guardianId, userId, VoiceCommandKey.OPEN_NAVIGATION,  "Open Navigation"),
            vc(guardianId, userId, VoiceCommandKey.OPEN_SETTINGS,    "Open Settings"),
            vc(guardianId, userId, VoiceCommandKey.REPEAT_LAST,      "Repeat"),
            vc(guardianId, userId, VoiceCommandKey.STOP_TTS,         "Stop")
        );
        voiceCommandConfigRepository.saveAll(defaults);

        // Hardware shortcuts default (Vol Up = Call Guardian, Vol Down = Start WalkGuide)
        List<HardwareShortcut> shortcuts = List.of(
            hs(guardianId, userId, HardwareShortcutKey.CALL_GUARDIAN,   "Volume Up",   24),
            hs(guardianId, userId, HardwareShortcutKey.START_WALKGUIDE, "Volume Down", 25),
            hs(guardianId, userId, HardwareShortcutKey.SEND_SOS,        null, null),
            hs(guardianId, userId, HardwareShortcutKey.STOP_WALKGUIDE,  null, null),
            hs(guardianId, userId, HardwareShortcutKey.OPEN_NOTIFICATION, null, null)
        );
        hardwareShortcutRepository.saveAll(shortcuts);

        // AI config default
        aiConfigRepository.save(AiConfig.builder()
                .guardianId(guardianId)
                .userId(userId)
                .build());
    }

    private VoiceCommandConfig vc(Long gId, Long uId, VoiceCommandKey key, String phrase) {
        return VoiceCommandConfig.builder()
                .guardianId(gId).userId(uId).commandKey(key).triggerPhrase(phrase).build();
    }

    private HardwareShortcut hs(Long gId, Long uId, HardwareShortcutKey key, String name, Integer code) {
        return HardwareShortcut.builder()
                .guardianId(gId).userId(uId).shortcutKey(key)
                .buttonName(name).buttonCode(code)
                .enabled(name != null).build();
    }

    private PairingStatusResponse buildStatus(PairingRelation p, User guardian, User user, String viewerRole) {
        String pairedWithName = "GUARDIAN".equals(viewerRole)
                ? user.getDisplayName() : guardian.getDisplayName();
        String pairedWithEmail = "GUARDIAN".equals(viewerRole)
                ? user.getEmail() : guardian.getEmail();
        return PairingStatusResponse.builder()
                .pairingId(p.getId())
                .status(p.getStatus().name())
                .pairedWithId("GUARDIAN".equals(viewerRole) ? user.getId() : guardian.getId())
                .pairedWithName(pairedWithName)
                .pairedWithEmail(pairedWithEmail)
                .uniqueUserId(user.getUniqueUserId())
                .invitedAt(p.getInvitedAt())
                .respondedAt(p.getRespondedAt())
                .build();
    }
}
