package at.procon.dip.testsupport;

import at.procon.dip.FixedPortPostgreSQLContainer;
import at.procon.dip.domain.document.repository.DocumentEmbeddingModelRepository;
import at.procon.dip.domain.document.repository.DocumentEmbeddingRepository;
import at.procon.dip.domain.document.repository.DocumentRepository;
import at.procon.dip.domain.document.repository.DocumentTextRepresentationRepository;
import at.procon.dip.domain.tenant.repository.DocumentTenantRepository;
import java.time.OffsetDateTime;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@SpringBootTest(classes = SearchSemanticTestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@Testcontainers
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestPropertySource(properties = {
        "spring.jpa.hibernate.ddl-auto=create-drop",
        "spring.jpa.show-sql=false",
        "spring.jpa.open-in-view=false",
        "spring.jpa.properties.hibernate.default_schema=DOC",
        "spring.main.lazy-initialization=true",
        "server.servlet.context-path=/api",

        "ted.search.default-page-size=20",
        "ted.search.max-page-size=100",
        "ted.search.fulltext-weight=0.35",
        "ted.search.trigram-weight=0.20",
        "ted.search.semantic-weight=0.45",
        "ted.search.recency-boost-weight=0.05",
        "ted.search.trigram-threshold=0.10",
        "ted.search.semantic-candidate-limit=50",
        "ted.search.similarity-threshold=0.01",

        "dip.embedding.enabled=true",
        "dip.embedding.default-document-model=mock-search",
        "dip.embedding.default-query-model=mock-search",
        "dip.embedding.jobs.enabled=true",
        "dip.embedding.jobs.batch-size=8",
        "dip.embedding.providers.mock-default.type=mock",
        "dip.embedding.providers.mock-default.dimensions=16",
        "dip.embedding.providers.mock-alt.type=mock",
        "dip.embedding.providers.mock-alt.dimensions=16",
        "dip.embedding.models.mock-search.provider-config-key=mock-default",
        "dip.embedding.models.mock-search.provider-model-key=mock-search",
        "dip.embedding.models.mock-search.dimensions=16",
        "dip.embedding.models.mock-search.supports-query-embedding-mode=true",
        "dip.embedding.models.mock-search.active=true",
        "dip.embedding.models.mock-search-alt.provider-config-key=mock-alt",
        "dip.embedding.models.mock-search-alt.provider-model-key=mock-search-alt",
        "dip.embedding.models.mock-search-alt.dimensions=16",
        "dip.embedding.models.mock-search-alt.supports-query-embedding-mode=true",
        "dip.embedding.models.mock-search-alt.active=true"
})
public abstract class AbstractSemanticSearchIntegrationTest {

    private static final int HOST_PORT = 15433;
    private static final String DB_NAME = "dip_semantic_search_test";
    private static final String DB_USER = "test";
    private static final String DB_PASSWORD = "test";
    private static final String JDBC_URL = "jdbc:postgresql://localhost:" + HOST_PORT + "/" + DB_NAME;

    @Container
    static PostgreSQLContainer<?> postgres = new FixedPortPostgreSQLContainer<>(
            DockerImageName.parse("pgvector/pgvector:pg16-trixie")
            .asCompatibleSubstituteFor("postgres").toString()
            , HOST_PORT)
            .withDatabaseName(DB_NAME)
            .withUsername(DB_USER)
            .withPassword(DB_PASSWORD)
            .withCommand("postgres", "-c", "fsync=off")
            .withInitScript("sql/create-doc-search-test-schemas.sql");


    @DynamicPropertySource
    static void registerProperties(DynamicPropertyRegistry registry) {
        if (!postgres.isRunning()) {
            postgres.start();
        }
        registry.add("spring.datasource.url", () -> JDBC_URL);
        registry.add("spring.datasource.username", () -> DB_USER);
        registry.add("spring.datasource.password", () -> DB_PASSWORD);
        registry.add("spring.datasource.driver-class-name", () -> "org.postgresql.Driver");
    }

    @Autowired
    protected JdbcTemplate jdbcTemplate;

    @Autowired
    protected DataSource dataSource;

    @Autowired
    protected DocumentRepository documentRepository;

    @Autowired
    protected DocumentTextRepresentationRepository representationRepository;

    @Autowired
    protected DocumentEmbeddingRepository embeddingRepository;

    @Autowired
    protected DocumentEmbeddingModelRepository embeddingModelRepository;

    @Autowired
    protected DocumentTenantRepository tenantRepository;

    @BeforeEach
    void resetSemanticSearchTestDatabase() {
        ensureSearchColumnsAndIndexes();
        cleanupDatabase();
    }

    protected void ensureSearchColumnsAndIndexes() {
        jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA doc");
        jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA public");
        jdbcTemplate.execute("ALTER TABLE doc.doc_text_representation ADD COLUMN IF NOT EXISTS search_config VARCHAR(64)");
        jdbcTemplate.execute("ALTER TABLE doc.doc_text_representation ADD COLUMN IF NOT EXISTS search_vector tsvector");
        jdbcTemplate.execute("CREATE INDEX IF NOT EXISTS idx_doc_text_repr_search_vector_test ON doc.doc_text_representation USING GIN (search_vector)");
        jdbcTemplate.execute("CREATE INDEX IF NOT EXISTS idx_doc_document_title_trgm_test ON doc.doc_document USING GIN (title doc.gin_trgm_ops)");
        jdbcTemplate.execute("CREATE INDEX IF NOT EXISTS idx_doc_document_summary_trgm_test ON doc.doc_document USING GIN (summary doc.gin_trgm_ops)");
        jdbcTemplate.execute("CREATE INDEX IF NOT EXISTS idx_doc_text_repr_text_trgm_test ON doc.doc_text_representation USING GIN (text_body doc.gin_trgm_ops)");
        jdbcTemplate.execute("ALTER TABLE doc.doc_embedding ADD COLUMN IF NOT EXISTS embedding_vector public.vector");
    }

    protected void cleanupDatabase() {
        jdbcTemplate.execute("TRUNCATE TABLE doc.doc_embedding_job, doc.doc_embedding, doc.doc_embedding_model, doc.doc_text_representation, doc.doc_document, doc.doc_tenant RESTART IDENTITY CASCADE");
    }

    protected void setDocumentCreatedAt(java.util.UUID documentId, OffsetDateTime createdAt) {
        jdbcTemplate.update("UPDATE doc.doc_document SET created_at = ?, updated_at = ? WHERE id = ?", createdAt, createdAt, documentId);
    }
}
