package com.walkguide.service;

import com.walkguide.dto.response.ActivityLogResponse;
import com.walkguide.dto.response.DashboardResponse;
import com.walkguide.entity.PairingRelation;
import com.walkguide.entity.SosEvent;
import com.walkguide.entity.User;
import com.walkguide.enums.PairingStatus;
import com.walkguide.enums.SosStatus;
import com.walkguide.repository.GuardianNotificationRepository;
import com.walkguide.repository.PairingRelationRepository;
import com.walkguide.repository.SosEventRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("GuardianDashboardService Unit Tests")
class GuardianDashboardServiceTest {

    @Mock PairingRelationRepository pairingRelationRepository;
    @Mock LocationService locationService;
    @Mock ActivityLogService activityLogService;
    @Mock SosEventRepository sosEventRepository;
    @Mock GuardianNotificationRepository notifRepository;

    @InjectMocks
    GuardianDashboardService guardianDashboardService;

    private User pairedUser;
    private PairingRelation activePairing;

    @BeforeEach
    void setUp() {
        pairedUser = User.builder()
                .id(5L)
                .email("user@test.com")
                .displayName("Test User")
                .role("ROLE_USER")
                .uniqueUserId("ABCDEF123456")
                .build();

        activePairing = PairingRelation.builder()
                .id(1L)
                .guardian(User.builder().id(10L).email("guardian@test.com").build())
                .user(pairedUser)
                .status(PairingStatus.ACTIVE)
                .build();
    }

    // ===== getDashboard TESTS =====

    @Test
    @DisplayName("getDashboard - tidak ada pairing aktif: return empty dashboard")
    void getDashboard_noPairing_shouldReturnEmptyDashboard() {
        when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE))
                .thenReturn(Optional.empty());

        DashboardResponse result = guardianDashboardService.getDashboard(10L);

        assertThat(result).isNotNull();
        assertThat(result.getPairedUserId()).isNull();
        assertThat(result.getRecentActivity()).isEmpty();
        assertThat(result.getUnreadSosCount()).isZero();
        assertThat(result.getUnreadNotifCount()).isZero();
    }

    @Test
    @DisplayName("getDashboard - ada pairing aktif: return data lengkap user")
    void getDashboard_withActivePairing_shouldReturnFullData() {
        when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE))
                .thenReturn(Optional.of(activePairing));
        when(locationService.getLastLocation(5L)).thenReturn(Optional.empty());
        when(activityLogService.getLogs(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L);

        DashboardResponse result = guardianDashboardService.getDashboard(10L);

        assertThat(result.getPairedUserId()).isEqualTo(5L);
        assertThat(result.getPairedUserName()).isEqualTo("Test User");
        assertThat(result.getPairedUserEmail()).isEqualTo("user@test.com");
        assertThat(result.getUniqueUserId()).isEqualTo("ABCDEF123456");
    }

    @Test
    @DisplayName("getDashboard - SOS berstatus TRIGGERED harus dihitung sebagai unread")
    void getDashboard_withTriggeredSos_shouldCountCorrectly() {
        SosEvent triggered = SosEvent.builder()
                .id(1L).userId(5L).status(SosStatus.TRIGGERED).build();
        SosEvent acknowledged = SosEvent.builder()
                .id(2L).userId(5L).status(SosStatus.ACKNOWLEDGED).build();
        SosEvent resolved = SosEvent.builder()
                .id(3L).userId(5L).status(SosStatus.RESOLVED).build();

        when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE))
                .thenReturn(Optional.of(activePairing));
        when(locationService.getLastLocation(5L)).thenReturn(Optional.empty());
        when(activityLogService.getLogs(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of(triggered, acknowledged, resolved)));
        when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L);

        DashboardResponse result = guardianDashboardService.getDashboard(10L);

        // Hanya yang TRIGGERED yang dihitung
        assertThat(result.getUnreadSosCount()).isEqualTo(1L);
    }

    @Test
    @DisplayName("getDashboard - unreadNotifCount harus sesuai repository")
    void getDashboard_withUnreadNotifs_shouldReturnCorrectCount() {
        when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE))
                .thenReturn(Optional.of(activePairing));
        when(locationService.getLastLocation(5L)).thenReturn(Optional.empty());
        when(activityLogService.getLogs(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(7L);

        DashboardResponse result = guardianDashboardService.getDashboard(10L);

        assertThat(result.getUnreadNotifCount()).isEqualTo(7L);
    }

    @Test
    @DisplayName("getDashboard - recentActivity harus mengambil max 5 log terbaru")
    void getDashboard_recentActivity_shouldFetchWithPageSizeFive() {
        ActivityLogResponse log1 = ActivityLogResponse.builder().id(1L).build();
        ActivityLogResponse log2 = ActivityLogResponse.builder().id(2L).build();

        when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE))
                .thenReturn(Optional.of(activePairing));
        when(locationService.getLastLocation(5L)).thenReturn(Optional.empty());
        when(activityLogService.getLogs(eq(5L), eq(PageRequest.of(0, 5))))
                .thenReturn(new PageImpl<>(List.of(log1, log2)));
        when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L);

        DashboardResponse result = guardianDashboardService.getDashboard(10L);

        assertThat(result.getRecentActivity()).hasSize(2);
        // Verifikasi dipanggil dengan PageRequest(0,5)
        verify(activityLogService).getLogs(eq(5L), eq(PageRequest.of(0, 5)));
    }

    @Test
    @DisplayName("getDashboard - semua SOS non-TRIGGERED tidak boleh dihitung sebagai unread")
    void getDashboard_allNonTriggeredSos_shouldReturnZeroUnread() {
        SosEvent ack = SosEvent.builder().id(1L).userId(5L).status(SosStatus.ACKNOWLEDGED).build();
        SosEvent res = SosEvent.builder().id(2L).userId(5L).status(SosStatus.RESOLVED).build();

        when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE))
                .thenReturn(Optional.of(activePairing));
        when(locationService.getLastLocation(5L)).thenReturn(Optional.empty());
        when(activityLogService.getLogs(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of()));
        when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class)))
                .thenReturn(new PageImpl<>(List.of(ack, res)));
        when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L);

        DashboardResponse result = guardianDashboardService.getDashboard(10L);

        assertThat(result.getUnreadSosCount()).isZero();
    }
}