package org.nstart.dep265.requestservice

import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.readText
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class MigrationSchemaTest {
    @Test
    fun `request service has a single fresh schema migration`() {
        val migrations = migrationDir()
            .toFile()
            .listFiles { file -> file.isFile && file.name.endsWith(".sql") }
            .orEmpty()
            .map { it.name }
            .sorted()

        assertEquals(listOf("V1__create_schema.sql"), migrations)
    }

    @Test
    fun `fresh schema defines mandatory cell importance columns`() {
        val schema = migration("V1__create_schema.sql").normalizedSql()
        val requests = schema.tableBlock("requests")
        val earthCells = schema.tableBlock("earth_cells")
        val requestCells = schema.tableBlock("request_cells")

        assertTrue(requests.contains("name varchar(255) not null"))
        assertTrue(requests.contains("constraint chk_requests_name_not_blank check (length(trim(name)) > 0)"))
        assertTrue(earthCells.contains("importance double precision not null default 0.0"))
        assertTrue(requestCells.contains("importance double precision not null"))
        assertTrue(!requestCells.contains("importance double precision not null default"))
    }

    @Test
    fun `earth cell importance trigger writes numeric zero when no request cells remain`() {
        val schema = migration("V1__create_schema.sql").normalizedSql()

        assertTrue(schema.contains("after insert or update or delete on request_cells"))
        assertTrue(schema.contains("coalesce(sum(request_cell.importance * (coalesce(request_cell.coverage_percent, 0.0) / 100.0)), 0.0)"))
    }

    @Test
    fun `outbox schema stores payload as jsonb and deduplicates request completion events`() {
        val schema = migration("V1__create_schema.sql").normalizedSql()
        val outboxEvents = schema.tableBlock("outbox_events")

        assertTrue(outboxEvents.contains("payload jsonb not null"))
        assertTrue(outboxEvents.contains("status varchar(32) not null"))
        assertTrue(outboxEvents.contains("attempts integer not null default 0"))
        assertTrue(outboxEvents.contains("next_attempt_at timestamp not null"))
        assertTrue(outboxEvents.contains("constraint uk_outbox_events_event_type_aggregate_id unique (event_type, aggregate_id)"))
        assertTrue(schema.contains("on outbox_events(status, created_at)"))
        assertTrue(schema.contains("on outbox_events(event_type, status, next_attempt_at, created_at)"))
    }

    @Test
    fun `request route matches schema stores suitability and applied coverage separately`() {
        val schema = migration("V1__create_schema.sql").normalizedSql()
        val requestRouteMatches = schema.tableBlock("request_route_matches")

        assertTrue(requestRouteMatches.contains("original_intersection_geometry text not null"))
        assertTrue(requestRouteMatches.contains("original_intersection_area double precision not null"))
        assertTrue(requestRouteMatches.contains("applied_intersection_geometry text null"))
        assertTrue(requestRouteMatches.contains("applied_intersection_area double precision not null default 0.0"))
        assertTrue(requestRouteMatches.contains("coverage_delta_percent double precision not null default 0.0"))
        assertTrue(requestRouteMatches.contains("contributes_to_coverage boolean not null default false"))
        assertTrue(requestRouteMatches.contains("constraint uk_request_route_match unique (request_id, route_id)"))
    }

    private fun migration(fileName: String): Path {
        val modulePath = Path.of("src/main/resources/db/migration", fileName)
        if (Files.exists(modulePath)) {
            return modulePath
        }
        return Path.of("services/pcp-request-service/src/main/resources/db/migration", fileName)
    }

    private fun migrationDir(): Path {
        val modulePath = Path.of("src/main/resources/db/migration")
        if (Files.exists(modulePath)) {
            return modulePath
        }
        return Path.of("services/pcp-request-service/src/main/resources/db/migration")
    }

    private fun Path.normalizedSql(): String =
        readText()
            .lowercase()
            .replace(Regex("\\s+"), " ")

    private fun String.tableBlock(tableName: String): String {
        val start = indexOf("create table if not exists $tableName")
        require(start >= 0) { "Table $tableName is not defined" }
        val nextTable = indexOf("create table if not exists", start + 1).takeIf { it >= 0 } ?: length
        return substring(start, nextTable)
    }
}
