/**
 * Copyright (c) 2025 SoftCorporation LLC. All rights reserved.
 *
 * The Software License, Version 1.0
 *
 * SoftCorporation LLC. grants you ("Licensee") a non-exclusive, royalty free,
 * license to use, modify and redistribute this software in source and binary
 * code form, provided that the following conditions are met:
 *
 * 1. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        SoftCorporation LLC. (http://www.softcorporation.com)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 2. This software and SoftCorporation name must not be used to
 *    promote products derived from the software without prior
 *    written permission. For written permission, please contact
 *    info@softcorporation.com.
 *
 * This software is provided "AS IS," without a warranty of any kind.
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 * IN NO EVENT SHALL THE SOFTCORPORATION BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION.
 *
 */
package com.softcorporation.sandi.search.service.solr;

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

import java.util.Arrays;
import java.util.List;

import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
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 com.softcorporation.sandi.base.model.Client;
import com.softcorporation.sandi.base.service.ClientService;
import com.softcorporation.sandi.base.service.LanguageModelService;
import com.softcorporation.sandi.base.service.solr.SolrService;
import com.softcorporation.sandi.base.util.Constants;
import com.softcorporation.sandi.search.model.SearchRequest;
import com.softcorporation.sandi.search.model.SearchResponse;
import com.softcorporation.sandi.search.util.SearchException;

/**
 * Unit tests for SolrSearchService
 */
@ExtendWith(MockitoExtension.class)
class SolrSearchServiceTest {

    @Mock
    private ClientService clientService;

    @Mock
    private SolrService solrService;

    @Mock
    private LanguageModelService languageModelService;

    @Mock
    private SolrClient solrClient;

    @Mock
    private QueryResponse queryResponse;

    @Mock
    private SolrDocumentList documentList;

    @InjectMocks
    private SolrSearchService solrSearchService;

    private Client testClient;
    private SearchRequest testSearchRequest;
    private String embString = "[-46,-5,-5,45,1,-16,49,4,14,-102,5,-3,60,-6,-7,89,-18,69,90,65,-37,-9,-43,28,-49,8,-78,93,38,-10,-30,3,17,-14,-23,-12,48,38,-2,11,39,57,-44,18,-74,14,25,-4,-12,-20,16,-38,-12,24,-1,-27,38,-37,13,-1,-43,41,-33,47,-5,62,6,2,-26,42,44,19,-44,2,-14,26,19,28,10,5,19,50,23,-3,0,-41,29,6,-66,-46,39,25,-24,-18,-24,-50,7,37,4,18,5,-53,-20,26,-15,43,-29,1,-82,-15,-17,5,4,-5,-19,1,-46,12,24,-43,-45,-72,-33,-17,26,30,-18,4,33,-16,21,-31,19,61,12,-10,-5,-36,22,17,-31,20,14,-55,-15,0,4,-6,7,60,44,18,-73,-3,3,-23,-15,4,0,26,19,15,4,-42,31,27,-13,-6,48,29,-32,12,-5,30,-38,-38,10,-16,11,11,-10,6,-35,0,-8,18,-11,-10,37,10,-58,34,56,37,-13,-22,13,-3,-29,41,-11,26,16,-9,4,14,-17,-7,1,10,-2,22,38,-5,-4,-11,3,58,-26,25,26,33,-3,-32,11,-36,5,-14,-32,34,20,4,33,37,24,-1,7,54,22,-33,-31,-31,-24,-7,-22,18,-15,-12,3,68,-3,-30,-4,6,-5,50,-36,-1,-38,0,-20,-14,-44,28,11,2,-5,30,-29,-27,-54,-17,-20,5,9,-5,-6,-76,34,61,-8,-31,-43,-25,29,11,-35,27,-45,27,11,18,-7,71,-41,-8,97,-27,19,45,17,9,6,-15,-32,-1,94,-1,56,-71,33,-34,-3,-19,-7,4,-8,2,-6,11,17,7,41,12,-75,-10,-38,-80,-20,30,12,47,33,11,-13,34,1,15,37,23,17,-60,-57,-18,31,19,3,13,34,-40,-37,37,28,28,-22,-8,-10,-12,24,52,-84,-28,34,3,-4,-19,-20,-128,48,24,-74,30,-17,-9,6,3,-46,-37,-25,-65,-38,40,46,15,6,-29,88,3,0,-2,-6,-47,9,-16,11,43,4,5,3,-2,-55,88,-40,1,-40,28,-29,-31,-45,-25,19,1,-37,19,47,-34,-50,1,-24,-12,6,38,-60,-21,33,-10,-31,-9,-20,39,-22,-42,35,-13,0,9,15,1,29,-2,22,-18,-40,-25,3,-32,-20,-18,19,20,-62,-34,-22,-51,-14,-13,-31,-49,-11,25,23,28,30,-35,7,102,48,25,-7,-22,19,66,42,38,-2,14,25,-19,-39,-69,8,-30,-28,-14,9,-18,-47,14,-24,-3,-18,13,17,-49,-37,11,19,22,-37,-25,-17,-21,11,45,84,-1,81,-47,-4,-13,26,-4,-13,8,-53,-7,-3,62,-48,-12,27,39,-15,49,-17,-5,-17,-21,-29,-10,-29,-19,-6,-8,-53,11,34,-27,16,-9,-34,19,-91,25,10,-21,4,17,-11,4,4,-28,43,-35,20,-19,-11,-4,16,14,2,-30,-8,8,16,-12,-7,64,32,-29,26,4,43,53,-6,21,-22,18,-16,15,23,-19,-36,-29,-6,33,44,33,1,-5,-6,-40,14,-3,10,-22,-27,-29,7,32,-8,-7,17,16,-30,-24,-12,-51,-28,-43,-10,-23,-19,49,35,28,25,-22,4,-43,33,36,-21,13,-33,-38,-28,-39,2,15,-30,45,27,-17,40,-1,-27,48,6,-10,-9,-36,-1,-15,-31,27,-57,2,19,-31,54,-19,15,49,4,0,11,-11,41,-31,8,3,23,12,2,2,24,-1,0,-26,19,10,6,-5,21,-39,-32,17,-32,-55,-19,-45,19,-28,-17,-17,-42,-15,39,-44,-17,18,-38,27,-11,19,52,-58,10,5,-12,-34,-10,-9,-29,-20,-50,-59,17,28,36,13,40,20,23,24,-19,-11,-33,-9,37,12,29,19,-27,5,-13,4,26,-26,64,8,26,-29,77,26,30,17,41,-48,-39,42,-17,5,-9,3,21,-6,39,21,18,-7,-37,3,-14,-36,-2,7,1,-6,-8,4,14,23,8,-17,-1,4,51,8,3,30,-10,-1,-11,-56,0,-45,31,25,-5,-33,67,-15,-42,-27,-7,-11,1,-1,-3,-42,7,30,8,68,-8,24,24,23,44,27,-94,-6,57,-1,-34,40,-12,-4,29,7,20,59,-25,-7,0,-10,-8,20,-66,-23,9,-15,-9,52,5,-9,-19,-19,3,-35,14,63,15,21,31,-4,26,-34,23,47,-9,-19,28,20,-3,43,-10,-23,-7,-1,0,2,39,2,-14,30,44,-18,-42,-24,-13,47,-6,-25,-39,1,8,0,8,19,9,21,-2,11,-10,2,17,29,-32,11,-21,15,6,3,-13,-60,-46,-44,32,31,4,-8,11,30,82,20,-32,12,17,34,-49,-1,37,-9,27,118,-41,7,-8,34,35,1,-71,-47,-17,24,21,-43,1,-53,28,-2,-37,-47,-26,-6,-14,-29,4,-17,76,55,-31,35,-13,-12,-10,-2,-2,-28,32,25,53,-56,69,-34,3,9,-28,11,17,-20,32,1,-10,-82,10,36,-16,-18,-47,61,-33,4,-34,-34,24,30,-41,-11,-32,-1,43,-27,7,46,28,35,-13,26,-15,0,-20,27,-40,-45,-18,16,5,17,-82,34,2,-25,-3,-15,48,12,-2,15,10,-23,19,3,15,-8,20,-10,7,35,48,-55,-9,16,31,-9,3,-38,-21,0,3,-3]";
    
    @BeforeEach
    void setUp() {
        testClient = new Client();
        testClient.setClientId("test-client");
        testClient.setName("Test Client");
        testClient.setCollection("test-collection");
        testClient.setActive(true);
        testClient.setLegacy(false);

        testSearchRequest = new SearchRequest();
        testSearchRequest.setRequestId("test-request");
        testSearchRequest.setClientId("test-client");
        testSearchRequest.setSearchQuery("test query");
        testSearchRequest.setPageNumber(1);
        testSearchRequest.setPageSize(10);
        testSearchRequest.setClient(testClient);
    }

    @Test
    void testSearch_Success() throws Exception {
        // Arrange
        when(solrService.getSolrClient()).thenReturn(solrClient);
        when(solrClient.query(anyString(), any(SolrQuery.class))).thenReturn(queryResponse);
        when(queryResponse.getQTime()).thenReturn(100);
        when(queryResponse.getResults()).thenReturn(documentList);
        when(queryResponse.getGroupResponse()).thenReturn(null);
        when(documentList.getNumFound()).thenReturn(5L);
        when(languageModelService.getQueryEmbeddingString(any(), anyString())).thenReturn(embString);

        // Act
        SearchResponse result = solrSearchService.search(testSearchRequest);

        // Assert
        assertNotNull(result);
        assertEquals("test-request", result.getRequestId());
        assertEquals("SUCCESS", result.getStatus());
        assertEquals(5L, result.getFoundResults());
        assertNotNull(result.getResults());

        verify(solrService).getSolrClient();
        verify(solrClient).query(anyString(), any(SolrQuery.class));
    }

    @Test
    void testSearch_WithNullQuery() throws Exception {
        // Arrange
        testSearchRequest.setSearchQuery(null);
        when(solrService.getSolrClient()).thenReturn(solrClient);
        when(solrClient.query(anyString(), any(SolrQuery.class))).thenReturn(queryResponse);
        when(queryResponse.getQTime()).thenReturn(100);
        when(queryResponse.getResults()).thenReturn(documentList);
        when(queryResponse.getGroupResponse()).thenReturn(null);
        when(documentList.getNumFound()).thenReturn(0L);

        // Act
        SearchResponse result = solrSearchService.search(testSearchRequest);

        // Assert
        assertNotNull(result);
        assertEquals("test-request", result.getRequestId());
        assertEquals("SUCCESS", result.getStatus());

        verify(solrService).getSolrClient();
    }

    @Test
    void testSearch_ThrowsException() throws Exception {
        // Arrange
        when(solrService.getSolrClient()).thenReturn(solrClient);
        when(solrClient.query(anyString(), any(SolrQuery.class)))
            .thenThrow(new RuntimeException("Solr connection failed"));

        // Act & Assert
        SearchException exception = assertThrows(SearchException.class, () -> {
            solrSearchService.search(testSearchRequest);
        });

        assertEquals("Solr connection failed", exception.getMessage());
        verify(solrService).getSolrClient();
    }

    @Test
    void testGetCollections_WithClientIds() throws Exception {
        // Arrange
        List<String> clientIds = Arrays.asList("client1", "client2");
        
        Client client1 = new Client();
        client1.setCollection("collection1");
        Client client2 = new Client();
        client2.setCollection("collection2");
        
        when(clientService.getClient("client1")).thenReturn(client1);
        when(clientService.getClient("client2")).thenReturn(client2);

        // Act
        String result = solrSearchService.getCollections(testSearchRequest, null, clientIds);

        // Assert
        assertEquals("collection1,collection2", result);
        verify(clientService).getClient("client1");
        verify(clientService).getClient("client2");
    }

    @Test
    void testGetCollections_WithCollectionList() throws Exception {
        // Arrange
        List<String> collectionList = Arrays.asList("collection1", "collection2");

        // Act
        String result = solrSearchService.getCollections(testSearchRequest, collectionList, null);

        // Assert
        assertEquals("collection1,collection2", result);
        verify(clientService, never()).getClient(any());
    }

    @Test
    void testGetCollections_DefaultToClientCollection() throws Exception {
        // Act
        String result = solrSearchService.getCollections(testSearchRequest, null, null);

        // Assert
        assertEquals("test-collection", result);
        verify(clientService, never()).getClient(any());
    }

    @Test
    void testGetFilters_WithFilterQuery() {
        // Arrange
        String filterQuery = "category:technology";

        // Act
        List<String> result = solrSearchService.getFilters(testSearchRequest, filterQuery, null);

        // Assert
        assertEquals(2, result.size());
        assertEquals("category:technology", result.get(0));
        assertEquals("_clientId:\"test-client\"", result.get(1));
    }

    @Test
    void testGetFilters_WithClientIds() {
        // Arrange
        List<String> clientIds = Arrays.asList("client1", "client2");

        // Act
        List<String> result = solrSearchService.getFilters(testSearchRequest, null, clientIds);

        // Assert
        assertEquals(1, result.size());
        assertEquals("_clientId:(\"client1\" \"client2\")", result.get(0));
    }

    @Test
    void testGetFilters_DefaultClientFilter() {
        // Act
        List<String> result = solrSearchService.getFilters(testSearchRequest, null, null);

        // Assert
        assertEquals(1, result.size());
        assertEquals("_clientId:\"test-client\"", result.get(0));
    }

//    @Test
    void testCustomSearch_Success() throws Exception {
        // Arrange
        testSearchRequest.setLegacy(true);
        testSearchRequest.setSynonyms(false);
        testSearchRequest.setSearchQuery("test");
        testSearchRequest.setCollections("test-collection");
        
        when(solrService.getSolrClient()).thenReturn(solrClient);
        when(solrClient.query(anyString(), any(SolrQuery.class))).thenReturn(queryResponse);
        when(queryResponse.getQTime()).thenReturn(50);
        when(queryResponse.getResults()).thenReturn(documentList);
        when(queryResponse.getGroupResponse()).thenReturn(null);
        when(documentList.getNumFound()).thenReturn(3L);

        // Act
        SearchResponse result = solrSearchService.customSearch(testSearchRequest);

        // Assert
        assertNotNull(result);
        assertEquals("test-request", result.getRequestId());
        assertEquals("SUCCESS", result.getStatus());
        assertEquals(3L, result.getFoundResults());

        verify(solrService).getSolrClient();
        verify(solrClient).query(anyString(), any(SolrQuery.class));
    }

    @Test
    void testLegacySearch_Success() throws Exception {
        // Arrange
        testSearchRequest.setLegacy(true);
        testSearchRequest.setSynonyms(false);
        testSearchRequest.setSearchQuery("test query");
        testSearchRequest.setCollections("test-collection");

        when(solrService.getSolrClient()).thenReturn(solrClient);
        when(solrClient.query(anyString(), any(SolrQuery.class))).thenReturn(queryResponse);
        when(queryResponse.getQTime()).thenReturn(75);
        when(queryResponse.getResults()).thenReturn(documentList);
        when(queryResponse.getGroupResponse()).thenReturn(null);
        when(documentList.getNumFound()).thenReturn(2L);
        when(documentList.getNumFound()).thenReturn(2L);

        // Act
        SearchResponse result = solrSearchService.search(testSearchRequest);

        // Assert
        assertNotNull(result);
        assertEquals("test-request", result.getRequestId());
        assertEquals("SUCCESS", result.getStatus());
        assertEquals(2L, result.getFoundResults());

        verify(solrService).getSolrClient();
    }
}