@file:OptIn(InternalSerializationApi::class)

package aza.instant

import com.ashampoo.kim.Kim
import com.ashampoo.kim.common.convertToPhotoMetadata
import com.ashampoo.kim.format.ImageMetadata
import com.ashampoo.kim.model.PhotoMetadata
import kotlinx.serialization.InternalSerializationApi
import java.security.MessageDigest
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.Base64
import java.util.Locale
import kotlin.math.roundToInt

fun formatExposureTime(t: Double): String {
    return if (t <= 0) t.toString()
    else if (t < 1.0) "1/${(1.0 / t).roundToInt()}s"
    else if (t < 10) "%.1fs".format(t)
    else "%.0fs".format(t)
}

data class ExifResult(val shutterInfo: String, val cameraInfo: String, val dateTime: String, val location: String, val urlName: String, val w: Int, val h: Int, val raw: ImageMetadata, val d: PhotoMetadata) {
    override fun toString() = "Date/Time: $dateTime\nLocation: $location\nCamera Info: $cameraInfo\nShutter Info: $shutterInfo\nURL Name: $urlName"
}

suspend fun parseExif(data: ByteArray): ExifResult? {
    val exif = Kim.readMetadata(data) ?: return null
    val d = exif.convertToPhotoMetadata()

    return ExifResult(
        shutterInfo = "${d.focalLength?.roundToInt()}mm  f/${d.fNumber}  ${formatExposureTime(d.exposureTime ?: 0.0)}  ISO-${d.iso}",
        // TODO: Un-hardcode this in the future
        cameraInfo = if (d.cameraModel == "ILCE-6700") "Sony ILCE-6700 + Tamron 18-300 F3.5-6.3" else listOfNotNull(
            listOfNotNull(d.cameraMake?.lowercase()?.replaceFirstChar { it.titlecase() }, d.cameraModel).ifEmpty { null }?.joinToString(" "),
            listOfNotNull(d.lensMake, d.lensModel).ifEmpty { null }?.joinToString(" "),
        ).joinToString(" + "),

        dateTime = run {
            val dateTime = LocalDateTime.ofEpochSecond((d.takenDate ?: System.currentTimeMillis()) / 1000, 0, java.time.ZoneOffset.UTC)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd  HH:mm"))
            val tz = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("z", Locale.US))
            "$dateTime  $tz"
        },

        location = d.gpsCoordinates?.let { coords ->
            getAddress(coords.latitude, coords.longitude)?.let {
                listOfNotNull(it.address.quarter, it.address.city, it.address.state, it.address.province, it.address.country)
                    .joinToString(", ").ifEmpty { it.display_name }.ifEmpty { null }
            }
        } ?: d.locationDisplay ?: "Unknown Location",

        // Create a 5-letter short-link ID
        // Hash the first 65536 bytes of the image data, and then take the first 5 letters of the Base32 encoding of the hash
        urlName = run {
            val hash = MessageDigest.getInstance("SHA-1").apply { update(data.sliceArray(0 until minOf(data.size, 65536))) }.digest()
            val base32 = Base64.getEncoder().encodeToString(hash).replace("=", "").replace("/", "").replace("+", "")
            base32.take(5)
        },

        w = d.widthPx!!,
        h = d.heightPx!!,
        raw = exif,
        d = d,
    )
}