/**
 * 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 acknowledg 
 *       "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.base.integration;

import static org.junit.jupiter.api.Assertions.*;

import java.util.List;
import java.util.stream.Collectors;

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.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

import com.softcorporation.sandi.base.model.Client;
import com.softcorporation.sandi.base.service.ClientService;
import com.softcorporation.sandi.base.service.solr.SolrService;
import com.softcorporation.sandi.base.util.Constants;

/**
 * Integration tests for Solr functionality
 * 
 * Note: These tests require a running Solr instance and are disabled by default.
 * To run these tests:
 * 1. Start a Solr instance on localhost:2181 (ZooKeeper) or configure appropriate hosts
 * 2. Create the required collections (_clients, _documents)
 * 3. Remove the @Disabled annotation
 * 4. Set up the test profile with appropriate Solr configuration
 */
@SpringBootTest
@ActiveProfiles("test")
@Disabled("Integration tests require running Solr instance")
class SolrIntegrationTest {

    @Autowired
    private SolrService solrService;

    @Autowired
    private ClientService clientService;

    private SolrClient solrClient;

    @BeforeEach
    void setUp() throws Exception {
        solrClient = solrService.getSolrClient();
        assertNotNull(solrClient, "SolrClient should be initialized");
        
        // Clean up any existing test data
        cleanupTestData();
    }

    @Test
    void testSolrConnection() throws Exception {
        // Act & Assert - Just verify we can get a client and it's not null
        assertNotNull(solrClient);
        
        // Try a simple ping or query to verify connection
        SolrQuery query = new SolrQuery("*:*");
        query.setRows(0); // We just want to test connection, not retrieve documents
        
        assertDoesNotThrow(() -> {
            QueryResponse response = solrClient.query(Constants.CLIENTS_COLLECTION, query);
            assertNotNull(response);
        });
    }

    @Test
    void testClientCRUD() throws Exception {
        // Arrange
        String testClientId = "test-integration-client";
        
        // Create test client document
        SolrInputDocument clientDoc = new SolrInputDocument();
        clientDoc.setField(Constants.FIELD_CLIENT_ID, testClientId);
        clientDoc.setField(Constants.FIELD_NAME, "Integration Test Client");
        clientDoc.setField(Constants.FIELD_COLLECTION, "test-collection");
        clientDoc.setField(Constants.FIELD_ACTIVE, true);
        clientDoc.setField(Constants.FIELD_LEGACY, false);
        clientDoc.setField(Constants.FIELDS_HIGH, "title");
        clientDoc.setField(Constants.FIELDS_LOW, "summary,tags");
        clientDoc.setField(Constants.FIELDS_CONTENT, "content");

        try {
            // Act - Create
            solrClient.add(Constants.CLIENTS_COLLECTION, clientDoc);
            solrClient.commit(Constants.CLIENTS_COLLECTION);

            // Act - Read
            Client retrievedClient = clientService.getClient(testClientId);

            // Assert
            assertNotNull(retrievedClient);
            assertEquals(testClientId, retrievedClient.getClientId());
            assertEquals("Integration Test Client", retrievedClient.getName());
            assertEquals("test-collection", retrievedClient.getCollection());
            assertTrue(retrievedClient.isActive());
            assertFalse(retrievedClient.isLegacy());
            assertEquals("title", retrievedClient.getFieldsHigh());
            assertEquals("summary,tags", retrievedClient.getFieldsLow());
            assertEquals("content", retrievedClient.getFieldsContent());

        } finally {
            // Cleanup
            solrClient.deleteByQuery(Constants.CLIENTS_COLLECTION, 
                Constants.FIELD_CLIENT_ID + ":" + testClientId);
            solrClient.commit(Constants.CLIENTS_COLLECTION);
        }
    }

    @Test
    void testGetAllClients() throws Exception {
        // Arrange - Create multiple test clients
        String[] testClientIds = {"test-client-1", "test-client-2", "test-client-3"};
        
        try {
            for (int i = 0; i < testClientIds.length; i++) {
                SolrInputDocument clientDoc = new SolrInputDocument();
                clientDoc.setField(Constants.FIELD_CLIENT_ID, testClientIds[i]);
                clientDoc.setField(Constants.FIELD_NAME, "Test Client " + (i + 1));
                clientDoc.setField(Constants.FIELD_COLLECTION, "test-collection-" + (i + 1));
                clientDoc.setField(Constants.FIELD_ACTIVE, i % 2 == 0); // Alternate active/inactive
                clientDoc.setField(Constants.FIELD_LEGACY, false);
                
                solrClient.add(Constants.CLIENTS_COLLECTION, clientDoc);
            }
            solrClient.commit(Constants.CLIENTS_COLLECTION);

            // Act
            List<Client> allClients = clientService.listClients();

            // Assert
            assertNotNull(allClients);
            assertTrue(allClients.size() >= 3); // At least our test clients

            // Filter active clients for verification
            List<Client> activeClients = allClients.stream()
                .filter(Client::isActive)
                .collect(Collectors.toList());
            
            assertNotNull(activeClients);
            assertTrue(activeClients.size() >= 2); // At least 2 active test clients

            // Verify our test clients are included
            long testClientsInAll = allClients.stream()
                .filter(client -> client.getClientId().startsWith("test-client-"))
                .count();
            assertEquals(3, testClientsInAll);

            long testClientsActive = activeClients.stream()
                .filter(client -> client.getClientId().startsWith("test-client-") && client.isActive())
                .count();
            assertEquals(2, testClientsActive); // test-client-1 and test-client-3 should be active

        } finally {
            // Cleanup
            for (String clientId : testClientIds) {
                solrClient.deleteByQuery(Constants.CLIENTS_COLLECTION, 
                    Constants.FIELD_CLIENT_ID + ":" + clientId);
            }
            solrClient.commit(Constants.CLIENTS_COLLECTION);
        }
    }

    @Test
    void testDocumentIndexing() throws Exception {
        // Arrange
        String testCollection = Constants.DEFAULT_COLLECTION;
        String testDocId = "test-integration-doc-1";
        
        SolrInputDocument doc = new SolrInputDocument();
        doc.setField("id", testDocId);
        doc.setField("title", "Integration Test Document");
        doc.setField("content", "This is test content for integration testing");
        doc.setField("_clientId", "test-client");
        doc.setField("_timestamp", System.currentTimeMillis());

        try {
            // Act - Index document
            solrClient.add(testCollection, doc);
            solrClient.commit(testCollection);

            // Act - Search for document
            SolrQuery query = new SolrQuery("id:" + testDocId);
            QueryResponse response = solrClient.query(testCollection, query);

            // Assert
            assertNotNull(response);
            SolrDocumentList results = response.getResults();
            assertEquals(1, results.size());

            SolrDocument retrievedDoc = results.get(0);
            assertEquals(testDocId, retrievedDoc.getFieldValue("id"));
            assertEquals("Integration Test Document", retrievedDoc.getFieldValue("title"));
            assertEquals("This is test content for integration testing", 
                retrievedDoc.getFieldValue("content"));
            assertEquals("test-client", retrievedDoc.getFieldValue("_clientId"));

        } finally {
            // Cleanup
            solrClient.deleteByQuery(testCollection, "id:" + testDocId);
            solrClient.commit(testCollection);
        }
    }

    @Test
    void testSearch() throws Exception {
        // Arrange
        String testCollection = Constants.DEFAULT_COLLECTION;
        String[] testDocIds = {"search-test-1", "search-test-2", "search-test-3"};
        
        try {
            // Create test documents
            for (int i = 0; i < testDocIds.length; i++) {
                SolrInputDocument doc = new SolrInputDocument();
                doc.setField("id", testDocIds[i]);
                doc.setField("title", "Search Test Document " + (i + 1));
                doc.setField("content", "This document contains search test content " + (i + 1));
                doc.setField("category", i % 2 == 0 ? "technology" : "science");
                doc.setField("_clientId", "test-client");
                
                solrClient.add(testCollection, doc);
            }
            solrClient.commit(testCollection);

            // Act - Search for documents
            SolrQuery query = new SolrQuery("content:\"search test\"");
            query.addFilterQuery("_clientId:test-client");
            query.setRows(10);
            
            QueryResponse response = solrClient.query(testCollection, query);

            // Assert
            assertNotNull(response);
            SolrDocumentList results = response.getResults();
            assertEquals(3, results.size());

            // Verify all our test documents are returned
            for (SolrDocument doc : results) {
                String docId = (String) doc.getFieldValue("id");
                assertTrue(docId.startsWith("search-test-"));
                assertEquals("test-client", doc.getFieldValue("_clientId"));
            }

            // Act - Test filtered search
            SolrQuery filteredQuery = new SolrQuery("*:*");
            filteredQuery.addFilterQuery("_clientId:test-client");
            filteredQuery.addFilterQuery("category:technology");
            
            QueryResponse filteredResponse = solrClient.query(testCollection, filteredQuery);
            
            // Assert - Should only get technology documents
            SolrDocumentList filteredResults = filteredResponse.getResults();
            assertEquals(2, filteredResults.size()); // search-test-1 and search-test-3
            
            for (SolrDocument doc : filteredResults) {
                assertEquals("technology", doc.getFieldValue("category"));
            }

        } finally {
            // Cleanup
            for (String docId : testDocIds) {
                solrClient.deleteByQuery(testCollection, "id:" + docId);
            }
            solrClient.commit(testCollection);
        }
    }

    @Test
    void testSolrServiceReset() throws Exception {
        // Arrange
        SolrClient originalClient = solrService.getSolrClient();
        assertNotNull(originalClient);

        // Act
        solrService.reset();

        // Assert
        SolrClient newClient = solrService.getSolrClient();
        assertNotNull(newClient);
        
        // Verify new client works
        SolrQuery query = new SolrQuery("*:*");
        query.setRows(0);
        
        assertDoesNotThrow(() -> {
            QueryResponse response = newClient.query(Constants.CLIENTS_COLLECTION, query);
            assertNotNull(response);
        });
    }

    @Test
    void testBatchOperations() throws Exception {
        // Arrange
        String testCollection = Constants.DEFAULT_COLLECTION;
        int batchSize = 5;
        String[] testDocIds = new String[batchSize];
        
        for (int i = 0; i < batchSize; i++) {
            testDocIds[i] = "batch-test-" + i;
        }

        try {
            // Act - Batch add documents
            for (int i = 0; i < batchSize; i++) {
                SolrInputDocument doc = new SolrInputDocument();
                doc.setField("id", testDocIds[i]);
                doc.setField("title", "Batch Test Document " + i);
                doc.setField("content", "Batch content " + i);
                doc.setField("_clientId", "batch-test-client");
                
                solrClient.add(testCollection, doc);
            }
            
            // Commit all at once
            solrClient.commit(testCollection);

            // Assert - Verify all documents were indexed
            SolrQuery query = new SolrQuery("_clientId:batch-test-client");
            query.setRows(batchSize + 1);
            
            QueryResponse response = solrClient.query(testCollection, query);
            SolrDocumentList results = response.getResults();
            
            assertEquals(batchSize, results.size());
            
            // Verify each document
            for (int i = 0; i < batchSize; i++) {
                boolean found = false;
                for (SolrDocument doc : results) {
                    if (testDocIds[i].equals(doc.getFieldValue("id"))) {
                        found = true;
                        assertEquals("Batch Test Document " + i, doc.getFieldValue("title"));
                        assertEquals("Batch content " + i, doc.getFieldValue("content"));
                        break;
                    }
                }
                assertTrue(found, "Document " + testDocIds[i] + " should be found");
            }

        } finally {
            // Cleanup
            solrClient.deleteByQuery(testCollection, "_clientId:batch-test-client");
            solrClient.commit(testCollection);
        }
    }

    private void cleanupTestData() throws Exception {
        // Clean up any test data that might be left from previous runs
        String[] testQueries = {
            Constants.FIELD_CLIENT_ID + ":test-*",
            Constants.FIELD_CLIENT_ID + ":*integration*",
            "id:test-*",
            "id:*integration*",
            "id:search-test-*",
            "id:batch-test-*",
            "_clientId:test-*",
            "_clientId:*test*"
        };

        for (String query : testQueries) {
            try {
                solrClient.deleteByQuery(Constants.CLIENTS_COLLECTION, query);
                solrClient.deleteByQuery(Constants.DEFAULT_COLLECTION, query);
            } catch (Exception e) {
                // Ignore cleanup errors
            }
        }
        
        try {
            solrClient.commit(Constants.CLIENTS_COLLECTION);
            solrClient.commit(Constants.DEFAULT_COLLECTION);
        } catch (Exception e) {
            // Ignore cleanup errors
        }
    }
}