package space.nstart.pcp.pcp_types_lib.utils

import java.time.Clock
import java.time.Instant
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException

/**
 * Shared UTC helpers for services that still keep LocalDateTime in domain and DB models.
 *
 * LocalDateTime values are treated as UTC-naive: the stored object has no zone, but its
 * numeric fields represent a UTC instant. Public API may receive either explicit UTC/offset
 * ISO strings or legacy naive ISO strings; explicit offsets are normalized to UTC.
 */
object UtcDateTimes {
    val UTC: ZoneOffset = ZoneOffset.UTC

    fun now(clock: Clock = Clock.systemUTC()): LocalDateTime =
        LocalDateTime.ofInstant(clock.instant(), UTC)

    fun parse(value: String): LocalDateTime {
        val normalized = normalize(value)

        parseOffset(normalized)?.let { return it }
        parseInstant(normalized)?.let { return it }
        parseLocal(normalized)?.let { return it }

        throw DateTimeParseException(
            "Expected ISO-8601 UTC/offset date-time or legacy UTC-naive date-time",
            value,
            0
        )
    }

    fun format(value: LocalDateTime): String = value.toInstant(UTC).toString()

    fun parseOrNull(value: String): LocalDateTime? = runCatching { parse(value) }.getOrNull()

    private fun normalize(value: String): String {
        val trimmed = value.trim().replace(Regex(" (\\d{2}:\\d{2})$"), "+\$1")
        return if (trimmed.length == 16) "$trimmed:00" else trimmed
    }

    private fun parseOffset(value: String): LocalDateTime? = runCatching {
        OffsetDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME)
            .withOffsetSameInstant(UTC)
            .toLocalDateTime()
    }.getOrNull()

    private fun parseInstant(value: String): LocalDateTime? = runCatching {
        LocalDateTime.ofInstant(Instant.parse(value), UTC)
    }.getOrNull()

    private fun parseLocal(value: String): LocalDateTime? = runCatching {
        LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    }.getOrNull()
}
