package at.procon.dip.search.integration;

import at.procon.dip.config.JacksonConfig;
import at.procon.dip.domain.document.DocumentFamily;
import at.procon.dip.domain.document.DocumentType;
import at.procon.dip.domain.document.RepresentationType;
import at.procon.dip.search.dto.SearchMode;
import at.procon.dip.search.dto.SearchRepresentationSelectionMode;
import at.procon.dip.search.dto.SearchRequest;
import at.procon.dip.testsupport.AbstractSearchIntegrationTest;
import at.procon.dip.testsupport.SearchTestDataFactory;
import at.procon.dip.testsupport.config.SearchTestConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Set;

import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;



@Import(SearchTestConfig.class)
@ImportAutoConfiguration({
        JacksonAutoConfiguration.class,
        HttpMessageConvertersAutoConfiguration.class,
        WebMvcAutoConfiguration.class
})
class GenericSearchEndpointIntegrationTest extends AbstractSearchIntegrationTest {

    @Autowired
    private SearchTestDataFactory dataFactory;

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void searchEndpoint_should_return_hits_for_fulltext_request() throws Exception {
        dataFactory.createDocumentWithPrimaryRepresentation(
                "Vienna school renovation framework",
                "School roof framework agreement",
                "Framework agreement for school roof renovation in Vienna.",
                DocumentType.TED_NOTICE,
                DocumentFamily.PROCUREMENT,
                "en",
                RepresentationType.SEMANTIC_TEXT);

        SearchRequest request = SearchRequest.builder()
                .queryText("framework agreement")
                .modes(Set.of(SearchMode.FULLTEXT))
                .representationSelectionMode(SearchRepresentationSelectionMode.PRIMARY_ONLY)
                .build();

        mockMvc.perform(post("/search")
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
                        .characterEncoding("UTF-8")
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.hits[0].title").value("Vienna school renovation framework"))
                .andExpect(jsonPath("$.enginesUsed[0]").value("POSTGRES_FULLTEXT"));
    }

    @Test
    void debugEndpoint_should_return_plan_and_engine_results() throws Exception {
        dataFactory.createDocumentWithPrimaryRepresentation(
                "Maintenance manual",
                "Factory maintenance manual",
                "Maintenance manual for calibration and preventive checks.",
                DocumentType.PDF,
                DocumentFamily.KNOWLEDGE,
                "en",
                RepresentationType.FULLTEXT);

        SearchRequest request = SearchRequest.builder()
                .queryText("maintenence manual")
                .modes(Set.of(SearchMode.HYBRID))
                .representationSelectionMode(SearchRepresentationSelectionMode.PRIMARY_ONLY)
                .build();

        mockMvc.perform(post("/search/debug")
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
                        .characterEncoding("UTF-8")
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.plan.engines").isArray())
                .andExpect(jsonPath("$.engineResults").isArray())
                .andExpect(jsonPath("$.fusedResponse.hits[0].title").value("Maintenance manual"));
    }

    @Test
    void metricsEndpoint_should_return_search_metrics_snapshot() throws Exception {
        dataFactory.createDocumentWithPrimaryAndChunks(
                "Energy optimization strategy",
                "Strategy overview",
                "This primary representation only contains a high level overview.",
                "en",
                List.of("District heating optimization strategy for municipal energy systems is described here."));

        SearchRequest request = SearchRequest.builder()
                .queryText("district heating optimization")
                .modes(Set.of(SearchMode.FULLTEXT))
                .representationSelectionMode(SearchRepresentationSelectionMode.PRIMARY_AND_CHUNKS)
                .build();

        mockMvc.perform(post("/search")
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
                        .characterEncoding("UTF-8")
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isOk());

        mockMvc.perform(get("/search/metrics"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.totalSearchRequests").isNumber())
                .andExpect(jsonPath("$.representationCounts").exists());
    }
}
