package com.saas.tenant.service;

import com.saas.tenant.entity.CalendarIntegration;
import com.saas.tenant.entity.InboundCallRequest;
import com.saas.tenant.repository.CalendarIntegrationRepository;
import com.saas.tenant.repository.InboundCallRequestRepository;
import org.junit.jupiter.api.BeforeEach;
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 java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class CalendarSyncServiceTest {

    @Mock
    private CalendarIntegrationRepository calendarIntegrationRepository;

    @Mock
    private InboundCallRequestRepository inboundCallRequestRepository;

    @InjectMocks
    private CalendarSyncService calendarSyncService;

    private InboundCallRequest testAppointment;
    private CalendarIntegration testIntegration;

    @BeforeEach
    void setUp() {
        testAppointment = new InboundCallRequest();
        testAppointment.setCallSid("test-call-123");
        testAppointment.setNom("Test Patient");
        testAppointment.setAppointmentDateTime(LocalDateTime.now().plusDays(1));
        testAppointment.setAppointmentConfirmed(true);
        testAppointment.setDoctorId(1L);

        testIntegration = CalendarIntegration.builder()
                .id(1L)
                .provider(CalendarIntegration.Provider.GOOGLE)
                .doctorId(1L)
                .accessToken("test-access-token")
                .refreshToken("test-refresh-token")
                .isActive(true)
                .build();
    }

    @Test
    void syncAppointment_WithDoctorId_QueriesDoctorCalendar() {
        // Given
        when(calendarIntegrationRepository.findByDoctorIdAndProvider(1L, CalendarIntegration.Provider.GOOGLE))
                .thenReturn(Optional.empty()); // No calendar, so no actual sync happens

        // When
        calendarSyncService.syncAppointment(testAppointment);

        // Then
        verify(calendarIntegrationRepository).findByDoctorIdAndProvider(1L, CalendarIntegration.Provider.GOOGLE);
    }

    @Test
    void syncAppointment_WithoutDoctorId_SkipsSync() {
        // Given
        testAppointment.setDoctorId(null);

        // When
        calendarSyncService.syncAppointment(testAppointment);

        // Then
        verify(calendarIntegrationRepository, never()).findByDoctorIdAndProvider(any(), any());
        verify(calendarIntegrationRepository, never()).findByProvider(any());
    }

    @Test
    void syncAppointment_NotConfirmed_SkipsSync() {
        // Given
        testAppointment.setAppointmentConfirmed(false);

        // When
        calendarSyncService.syncAppointment(testAppointment);

        // Then
        verify(calendarIntegrationRepository, never()).findByDoctorIdAndProvider(any(), any());
    }

    @Test
    void syncAppointment_NoDateTime_SkipsSync() {
        // Given
        testAppointment.setAppointmentDateTime(null);

        // When
        calendarSyncService.syncAppointment(testAppointment);

        // Then
        verify(calendarIntegrationRepository, never()).findByDoctorIdAndProvider(any(), any());
    }

    @Test
    void syncAllAppointmentsForDoctor_QueriesCorrectRepository() {
        // Given
        Long doctorId = 1L;
        InboundCallRequest appointment1 = createAppointment("call-1", doctorId, true);
        InboundCallRequest appointment2 = createAppointment("call-2", doctorId, true);

        List<InboundCallRequest> appointments = Arrays.asList(appointment1, appointment2);

        when(inboundCallRequestRepository
                .findByDoctorIdAndAppointmentConfirmedTrueAndAppointmentDateTimeNotNull(doctorId))
                .thenReturn(appointments);
        when(calendarIntegrationRepository.findByDoctorIdAndProvider(doctorId, CalendarIntegration.Provider.GOOGLE))
                .thenReturn(Optional.empty()); // No calendar, so no actual sync

        // When
        int syncedCount = calendarSyncService.syncAllAppointmentsForDoctor(doctorId);

        // Then
        verify(inboundCallRequestRepository)
                .findByDoctorIdAndAppointmentConfirmedTrueAndAppointmentDateTimeNotNull(doctorId);
        // Since no calendar is connected, synced count will be 0
        assertEquals(0, syncedCount);
    }

    @Test
    void syncAllAppointmentsForDoctor_NoAppointments_ReturnsZero() {
        // Given
        Long doctorId = 1L;
        when(inboundCallRequestRepository
                .findByDoctorIdAndAppointmentConfirmedTrueAndAppointmentDateTimeNotNull(doctorId))
                .thenReturn(List.of());

        // When
        int syncedCount = calendarSyncService.syncAllAppointmentsForDoctor(doctorId);

        // Then
        assertEquals(0, syncedCount);
        verify(inboundCallRequestRepository)
                .findByDoctorIdAndAppointmentConfirmedTrueAndAppointmentDateTimeNotNull(doctorId);
    }

    @Test
    void syncAppointment_NoDoctorCalendar_DoesNotThrowException() {
        // Given
        when(calendarIntegrationRepository.findByDoctorIdAndProvider(1L, CalendarIntegration.Provider.GOOGLE))
                .thenReturn(Optional.empty());

        // When & Then - should not throw exception
        assertDoesNotThrow(() -> calendarSyncService.syncAppointment(testAppointment));

        verify(calendarIntegrationRepository).findByDoctorIdAndProvider(1L, CalendarIntegration.Provider.GOOGLE);
    }

    private InboundCallRequest createAppointment(String callSid, Long doctorId, boolean confirmed) {
        InboundCallRequest appointment = new InboundCallRequest();
        appointment.setCallSid(callSid);
        appointment.setDoctorId(doctorId);
        appointment.setAppointmentConfirmed(confirmed);
        appointment.setAppointmentDateTime(LocalDateTime.now().plusDays(1));
        appointment.setNom("Test Patient");
        return appointment;
    }
}
