package space.nstart.pcp.pcp_satellites_service.service

import org.locationtech.jts.io.WKTReader
import org.springframework.stereotype.Component
import space.nstart.pcp.pcp_satellites_service.entity.ComplexPlanRunEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeBookedSlotEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeCellEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeSnapshotEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeType
import space.nstart.pcp.pcp_satellites_service.model.mode.SurveyMode
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO

@Component
class SatelliteModeMapper {

    fun toEntity(
        satelliteId: Long,
        survey: SurveyMode,
        snapshot: SatelliteModeSnapshotEntity? = null,
        complexPlanRun: ComplexPlanRunEntity? = null
    ): SatelliteModeEntity {
        val (latitude, longitude) = resolveCoordinates(survey)
        val entity = SatelliteModeEntity(
            satelliteId = satelliteId,
            source = survey.source,
            type = SatelliteModeType.SURVEY,
            startTime = survey.time,
            endTime = survey.timeStop,
            revolution = survey.revolution,
            lat = latitude,
            longitude = longitude,
            duration = survey.duration,
            contourWkt = survey.contourWKT,
            roll = survey.roll,
            cellNum = survey.primaryCellNum(),
            snapshot = snapshot,
            complexPlanRun = complexPlanRun
        )

        entity.bookedSlots = survey.bookedSlotIds
            .filter { it > 0 }
            .distinct()
            .map { bookedSlotId ->
                SatelliteModeBookedSlotEntity(
                    mode = entity,
                    bookedSlotId = bookedSlotId
                )
            }
            .toMutableList()

        entity.cells = survey.cellNumsForPersistence()
            .map { cellNum ->
                SatelliteModeCellEntity(
                    mode = entity,
                    cellNum = cellNum
                )
            }
            .toMutableList()

        return entity
    }

    fun toSurveyMode(
        entity: SatelliteModeEntity,
        bookedSlotIds: List<Long> = emptyList(),
        cellNums: List<Long> = emptyList()
    ): SurveyMode =
        SurveyMode(
            revolution = entity.revolution,
            time = entity.startTime,
            timeStop = entity.endTime,
            roll = entity.roll,
            latitude = entity.lat,
            longitude = entity.longitude,
            duration = entity.duration,
            contourWKT = entity.contourWkt ?: "",
            cellNum = primaryCellNum(cellNums, entity.cellNum),
            source = entity.source,
            bookedSlotIds = bookedSlotIds,
            cellNums = normalizeCellNums(cellNums, entity.cellNum)
        )

    fun toResponse(
        entity: SatelliteModeEntity,
        bookedSlotIds: List<Long> = emptyList(),
        cellNums: List<Long> = emptyList()
    ): SatelliteModeResponseDTO =
        SatelliteModeResponseDTO(
            id = entity.id ?: 0,
            satelliteId = entity.satelliteId,
            source = entity.source.name,
            type = entity.type.name,
            startTime = entity.startTime,
            endTime = entity.endTime,
            revolution = entity.revolution,
            lat = entity.lat,
            longitude = entity.longitude,
            duration = entity.duration,
            contourWkt = entity.contourWkt,
            roll = entity.roll,
            cellNum = primaryCellNum(cellNums, entity.cellNum),
            cellNums = normalizeCellNums(cellNums, entity.cellNum),
            bookedSlotIds = bookedSlotIds,
            snapshotId = entity.snapshot?.id
        )

    private fun SurveyMode.primaryCellNum(): Long =
        cellNumsForPersistence().singleOrNull() ?: -1

    private fun SurveyMode.cellNumsForPersistence(): List<Long> =
        normalizeCellNums(cellNums, cellNum)

    private fun normalizeCellNums(cellNums: List<Long>, fallbackCellNum: Long): List<Long> {
        val normalized = cellNums
            .ifEmpty { if (fallbackCellNum > 0) listOf(fallbackCellNum) else emptyList() }
            .filter { it > 0 }
            .distinct()
            .sorted()

        return normalized
    }

    private fun primaryCellNum(cellNums: List<Long>, fallbackCellNum: Long): Long =
        normalizeCellNums(cellNums, fallbackCellNum).singleOrNull() ?: -1

    private fun resolveCoordinates(survey: SurveyMode): Pair<Double, Double> {
        if (survey.latitude != 0.0 || survey.longitude != 0.0) {
            return survey.latitude to survey.longitude
        }
        if (survey.contourWKT.isBlank()) {
            return survey.latitude to survey.longitude
        }

        return runCatching {
            val centroid = WKTReader().read(survey.contourWKT).centroid
            centroid.y to centroid.x
        }.getOrDefault(survey.latitude to survey.longitude)
    }
}
