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

import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.common.SolrInputDocument;
import org.json.JSONObject;
import org.junit.jupiter.api.AfterEach;
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.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import com.softcorporation.sandi.base.service.solr.SolrService;
import com.softcorporation.sandi.base.util.Constants;
import com.softcorporation.sandi.search.SearchApplication;

/**
 * Integration tests for Search API functionality
 * 
 * Note: These tests require a running Solr instance and are disabled by default.
 * To run these tests:
 * 1. Start a Solr instance
 * 2. Create the required collections 
 * 3. Remove the @Disabled annotation
 */
@SpringBootTest(classes = SearchApplication.class)
@AutoConfigureWebMvc
@ActiveProfiles("test")
@Disabled("Integration tests require running Solr instance")
class SearchIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private SolrService solrService;

    private SolrClient solrClient;
    private final String TEST_CLIENT_ID = "search-integration-client";
    private final String TEST_COLLECTION = "test-search-collection";

    @BeforeEach
    void setUp() throws Exception {
        solrClient = solrService.getSolrClient();
        
        // Create test client
        createTestClient();
        
        // Create test documents
        createTestDocuments();
    }

    @AfterEach
    void tearDown() throws Exception {
        // Cleanup test data
        cleanupTestData();
    }

    @Test
    void testSearchEndpoint_BasicSearch() throws Exception {
        String requestBody = """
            {
                "query": "integration",
                "clientId": "%s",
                "start": 0,
                "rows": 10
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.query", is("integration")))
                .andExpect(jsonPath("$.total", greaterThan(0)))
                .andExpect(jsonPath("$.start", is(0)))
                .andExpect(jsonPath("$.rows", is(10)))
                .andExpect(jsonPath("$.documents", isA(JSONObject[].class)))
                .andExpect(jsonPath("$.documents", hasSize(greaterThan(0))));
    }

    @Test
    void testSearchEndpoint_WithFilters() throws Exception {
        String requestBody = """
            {
                "query": "*:*",
                "clientId": "%s",
                "start": 0,
                "rows": 10,
                "filters": {
                    "category": "technology"
                }
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.total", greaterThan(0)))
                .andExpect(jsonPath("$.documents", hasSize(greaterThan(0))));
        
        // Verify all returned documents have category=technology
        // Note: This would require custom result matchers for deeper validation
    }

    @Test
    void testSearchEndpoint_WithSorting() throws Exception {
        String requestBody = """
            {
                "query": "*:*",
                "clientId": "%s",
                "start": 0,
                "rows": 10,
                "sort": "title asc"
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.documents", hasSize(greaterThan(0))));
    }

    @Test
    void testSearchEndpoint_Pagination() throws Exception {
        // First page
        String firstPageRequest = """
            {
                "query": "*:*",
                "clientId": "%s",
                "start": 0,
                "rows": 2
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(firstPageRequest))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.start", is(0)))
                .andExpect(jsonPath("$.rows", is(2)))
                .andExpect(jsonPath("$.documents", hasSize(2)));

        // Second page
        String secondPageRequest = """
            {
                "query": "*:*",
                "clientId": "%s",
                "start": 2,
                "rows": 2
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(secondPageRequest))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.start", is(2)))
                .andExpect(jsonPath("$.rows", is(2)));
    }

    @Test
    void testSearchEndpoint_EmptyQuery() throws Exception {
        String requestBody = """
            {
                "query": "",
                "clientId": "%s",
                "start": 0,
                "rows": 10
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.error", containsString("Query cannot be empty")));
    }

    @Test
    void testSearchEndpoint_NonExistentClient() throws Exception {
        String requestBody = """
            {
                "query": "test",
                "clientId": "non-existent-client",
                "start": 0,
                "rows": 10
            }
            """;

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isNotFound())
                .andExpect(jsonPath("$.error", containsString("Client not found")));
    }

    @Test
    void testSearchEndpoint_InvalidPagination() throws Exception {
        String requestBody = """
            {
                "query": "test",
                "clientId": "%s",
                "start": -1,
                "rows": 10
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.error", containsString("Start parameter must be non-negative")));
    }

    @Test
    void testGetDocumentEndpoint_Success() throws Exception {
        mockMvc.perform(get("/document/{clientId}/{documentId}", TEST_CLIENT_ID, "integration-doc-1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id", is("integration-doc-1")))
                .andExpect(jsonPath("$.title", is("Integration Test Document 1")))
                .andExpect(jsonPath("$.content", containsString("integration test")));
    }

    @Test
    void testGetDocumentEndpoint_NotFound() throws Exception {
        mockMvc.perform(get("/document/{clientId}/{documentId}", TEST_CLIENT_ID, "non-existent-doc"))
                .andExpect(status().isNotFound())
                .andExpect(jsonPath("$.error", containsString("Document not found")));
    }

    @Test
    void testGetDocumentEndpoint_ClientNotFound() throws Exception {
        mockMvc.perform(get("/document/{clientId}/{documentId}", "non-existent-client", "doc-1"))
                .andExpect(status().isNotFound())
                .andExpect(jsonPath("$.error", containsString("Client not found")));
    }

    @Test
    void testSearchEndpoint_SpecialCharacters() throws Exception {
        String requestBody = """
            {
                "query": "special & characters * test",
                "clientId": "%s",
                "start": 0,
                "rows": 10
            }
            """.formatted(TEST_CLIENT_ID);

        // Should handle special characters gracefully
        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.query", is("special & characters * test")));
    }

    @Test
    void testSearchEndpoint_LargeResultSet() throws Exception {
        String requestBody = """
            {
                "query": "*:*",
                "clientId": "%s",
                "start": 0,
                "rows": 1000
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.rows", is(1000)));
    }

    @Test
    void testSearchEndpoint_ExceedsMaxRows() throws Exception {
        String requestBody = """
            {
                "query": "*:*",
                "clientId": "%s",
                "start": 0,
                "rows": 10001
            }
            """.formatted(TEST_CLIENT_ID);

        mockMvc.perform(post("/search")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.error", containsString("Maximum rows allowed")));
    }

    private void createTestClient() throws Exception {
        SolrInputDocument clientDoc = new SolrInputDocument();
        clientDoc.setField(Constants.FIELD_CLIENT_ID, TEST_CLIENT_ID);
        clientDoc.setField(Constants.FIELD_NAME, "Search 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, "tags,category");
        clientDoc.setField(Constants.FIELDS_CONTENT, "summary,content");

        solrClient.add(Constants.CLIENTS_COLLECTION, clientDoc);
        solrClient.commit(Constants.CLIENTS_COLLECTION);
    }

    private void createTestDocuments() throws Exception {
        // Create test documents with various content
        String[] titles = {
            "Integration Test Document 1",
            "Technology Integration Guide", 
            "Science Research Paper",
            "Business Integration Strategy",
            "Special Characters & Symbols Test"
        };
        
        String[] contents = {
            "This is integration test content for document 1",
            "Technology integration guide with detailed examples",
            "Scientific research on integration methodologies",
            "Business strategy for system integration projects",
            "Testing special characters: @#$%^&*()_+-=[]{}|;:,.<>?"
        };
        
        String[] categories = {"general", "technology", "science", "business", "testing"};
        
        for (int i = 0; i < titles.length; i++) {
            SolrInputDocument doc = new SolrInputDocument();
            doc.setField("id", "integration-doc-" + (i + 1));
            doc.setField("title", titles[i]);
            doc.setField("content", contents[i]);
            doc.setField("category", categories[i]);
            doc.setField("author", "Test Author " + (i + 1));
            doc.setField("_clientId", TEST_CLIENT_ID);
            doc.setField("_timestamp", System.currentTimeMillis());
            
            solrClient.add(TEST_COLLECTION, doc);
        }
        
        solrClient.commit(TEST_COLLECTION);
    }

    private void cleanupTestData() throws Exception {
        try {
            // Delete test client
            solrClient.deleteByQuery(Constants.CLIENTS_COLLECTION, 
                Constants.FIELD_CLIENT_ID + ":" + TEST_CLIENT_ID);
            solrClient.commit(Constants.CLIENTS_COLLECTION);
            
            // Delete test documents
            solrClient.deleteByQuery(TEST_COLLECTION, "_clientId:" + TEST_CLIENT_ID);
            solrClient.commit(TEST_COLLECTION);
        } catch (Exception e) {
            // Ignore cleanup errors
            System.err.println("Warning: Failed to cleanup test data: " + e.getMessage());
        }
    }
}