package com.saas.voip.service;

import com.saas.admin.dto.StartVapiCallDTO;
import com.saas.admin.dto.VapiCallDTO;
import com.saas.shared.service.EventPublisherService;
import com.saas.voip.dto.CallRequest;
import com.saas.voip.dto.CallResponse;
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 static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class VoIPOrchestrationServiceTest {
    
    @Mock
    private VapiService vapiService;
    
    @Mock
    private RetellApiClient retellApiClient;
    
    @Mock
    private EventPublisherService eventPublisher;
    
    @InjectMocks
    private VoIPOrchestrationService orchestrationService;
    
    private CallRequest testCallRequest;
    private VapiCallDTO testVapiCall;
    private CallResponse testRetellResponse;
    
    @BeforeEach
    void setUp() {
        testCallRequest = CallRequest.builder()
                .tenantId("tenant-123")
                .assistantId("assistant-456")
                .phoneNumber("+33612345678")
                .build();
        
        testVapiCall = new VapiCallDTO();
        testVapiCall.setVapiCallId("vapi-call-789");
        testVapiCall.setStatus("in-progress");
        
        testRetellResponse = CallResponse.builder()
                .callId("retell-call-123")
                .provider("RETELL")
                .status("in-progress")
                .phoneNumber("+33612345678")
                .build();
    }
    
    @Test
    void testInitiateVapiCall_Success() {
        when(vapiService.startCall(eq("tenant-123"), any(StartVapiCallDTO.class)))
                .thenReturn(testVapiCall);
        
        CallResponse response = orchestrationService.initiateVapiCall(testCallRequest);
        
        assertNotNull(response);
        assertEquals("vapi-call-789", response.getCallId());
        assertEquals("VAPI", response.getProvider());
        assertEquals("in-progress", response.getStatus());
        assertEquals("+33612345678", response.getPhoneNumber());
        
        verify(vapiService).startCall(eq("tenant-123"), any(StartVapiCallDTO.class));
    }
    
    @Test
    void testInitiateVapiCall_Failure() {
        when(vapiService.startCall(eq("tenant-123"), any(StartVapiCallDTO.class)))
                .thenThrow(new RuntimeException("Vapi API error"));
        
        assertThrows(RuntimeException.class, () -> 
            orchestrationService.initiateVapiCall(testCallRequest)
        );
    }
    
    @Test
    void testInitiateRetellCall_Success() {
        when(retellApiClient.initiateCall(any(), any(), any(), any(), any()))
                .thenReturn(testRetellResponse);
        
        CallResponse response = orchestrationService.initiateRetellCall(testCallRequest);
        
        assertNotNull(response);
        assertEquals("retell-call-123", response.getCallId());
        assertEquals("RETELL", response.getProvider());
        
        verify(retellApiClient).initiateCall(any(), eq("+33612345678"), eq("assistant-456"), any(), any());
    }
    
    @Test
    void testInitiateRetellCall_Failure() {
        when(retellApiClient.initiateCall(any(), any(), any(), any(), any()))
                .thenThrow(new RuntimeException("Retell API error"));
        
        assertThrows(RuntimeException.class, () -> 
            orchestrationService.initiateRetellCall(testCallRequest)
        );
    }
    
    @Test
    void testRouteCall_ToVapi() {
        when(vapiService.startCall(eq("tenant-123"), any(StartVapiCallDTO.class)))
                .thenReturn(testVapiCall);
        
        CallResponse response = orchestrationService.routeCall(testCallRequest, "VAPI");
        
        assertNotNull(response);
        assertEquals("VAPI", response.getProvider());
        verify(vapiService).startCall(eq("tenant-123"), any(StartVapiCallDTO.class));
    }
    
    @Test
    void testRouteCall_ToRetell() {
        when(retellApiClient.initiateCall(any(), any(), any(), any(), any()))
                .thenReturn(testRetellResponse);
        
        CallResponse response = orchestrationService.routeCall(testCallRequest, "RETELL");
        
        assertNotNull(response);
        assertEquals("RETELL", response.getProvider());
        verify(retellApiClient).initiateCall(any(), eq("+33612345678"), eq("assistant-456"), any(), any());
    }
    
    @Test
    void testRouteCall_ToTwilio_NotSupported() {
        assertThrows(UnsupportedOperationException.class, () -> 
            orchestrationService.routeCall(testCallRequest, "TWILIO")
        );
    }
    
    @Test
    void testRouteCall_ToTelnyx_NotSupported() {
        assertThrows(UnsupportedOperationException.class, () -> 
            orchestrationService.routeCall(testCallRequest, "TELNYX")
        );
    }
    
    @Test
    void testRouteCall_UnknownProvider() {
        assertThrows(IllegalArgumentException.class, () -> 
            orchestrationService.routeCall(testCallRequest, "UNKNOWN")
        );
    }
    
    @Test
    void testRouteCall_CaseInsensitive() {
        when(vapiService.startCall(eq("tenant-123"), any(StartVapiCallDTO.class)))
                .thenReturn(testVapiCall);
        
        CallResponse response = orchestrationService.routeCall(testCallRequest, "vapi");
        
        assertNotNull(response);
        assertEquals("VAPI", response.getProvider());
    }
    
    @Test
    void testGetCallStatus_Vapi() {
        VapiCallDTO callDto = new VapiCallDTO();
        callDto.setStatus("completed");
        when(vapiService.getCall("tenant-123", "call-123")).thenReturn(callDto);
        
        String status = orchestrationService.getCallStatus("call-123", "VAPI", "tenant-123");
        
        assertEquals("completed", status);
    }
    
    @Test
    void testGetCallStatus_Retell() {
        when(retellApiClient.getCallStatus("call-123")).thenReturn("ended");
        
        String status = orchestrationService.getCallStatus("call-123", "RETELL", "tenant-123");
        
        assertEquals("ended", status);
    }
    
    @Test
    void testGetCallStatus_UnknownProvider() {
        assertThrows(IllegalArgumentException.class, () -> 
            orchestrationService.getCallStatus("call-123", "UNKNOWN", "tenant-123")
        );
    }
    
    @Test
    void testTerminateCall_Retell() {
        assertDoesNotThrow(() -> 
            orchestrationService.terminateCall("call-123", "RETELL")
        );
        
        verify(retellApiClient).terminateCall("call-123");
    }
    
    @Test
    void testTerminateCall_Vapi_NotSupported() {
        assertThrows(UnsupportedOperationException.class, () -> 
            orchestrationService.terminateCall("call-123", "VAPI")
        );
    }
    
    @Test
    void testTerminateCall_UnknownProvider() {
        assertThrows(IllegalArgumentException.class, () -> 
            orchestrationService.terminateCall("call-123", "UNKNOWN")
        );
    }
}
