package su.reya.coop

import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.number
import kotlinx.datetime.toLocalDateTime
import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.Timestamp
import rust.nostr.sdk.UnsignedEvent
import kotlin.time.Clock
import kotlin.time.Instant

enum class RoomKind {
    Ongoing,
    Request;

    companion object {
        fun default(): RoomKind = Request
    }
}

data class Room(
    val id: Long,
    val createdAt: Timestamp,
    val subject: String?,
    val members: Set<PublicKey>,
    val kind: RoomKind = RoomKind.default(),
    val lastMessage: String? = null
) : Comparable<Room> {
    override fun compareTo(other: Room): Int {
        return this.createdAt.asSecs().compareTo(other.createdAt.asSecs())
    }

    companion object {
        fun new(rumor: UnsignedEvent, userPubkey: PublicKey): Room {
            val id = rumor.roomId()
            val createdAt = rumor.createdAt()
            val subject = rumor.tags().toVec().find { it.kind() == "subject" }?.content()

            // Collect the author's public key and all public keys from tags
            val pubkeys: MutableSet<PublicKey> = mutableSetOf()
            pubkeys.add(rumor.author())
            pubkeys.addAll(rumor.tags().publicKeys())

            // Also remove the user's public key from the list, current user is always a member
            if (pubkeys.size > 1 && pubkeys.contains(userPubkey)) {
                pubkeys.remove(userPubkey)
            }

            // Create a new Room instance
            return Room(
                id = id,
                createdAt = createdAt,
                subject = subject,
                members = pubkeys as Set<PublicKey>,
                lastMessage = rumor.content()
            )
        }
    }

    fun setKind(kind: RoomKind): Room {
        return this.copy(kind = kind)
    }

    fun setCreatedAt(createdAt: Timestamp): Room {
        return this.copy(createdAt = createdAt)
    }

    fun setSubject(subject: String?): Room {
        return this.copy(subject = subject)
    }

    fun setLastMessage(message: String?): Room {
        return this.copy(lastMessage = message)
    }

    fun isGroup(): Boolean {
        return members.size > 1
    }
}

fun UnsignedEvent.roomId(): Long {
    // Collect the author's public key and all public keys from tags
    val pubkeys: MutableList<PublicKey> = mutableListOf()
    pubkeys.add(this.author())
    pubkeys.addAll(this.tags().publicKeys())

    // Sort and hash the list of public keys
    val sortedUniqueKeys = pubkeys
        .distinctBy { it.toBech32() }
        .sortedBy { it.toBech32() }

    return sortedUniqueKeys.hashCode().toLong()
}

fun Timestamp.ago(): String {
    val SECONDS_IN_MINUTE = 60L
    val MINUTES_IN_HOUR = 60L
    val HOURS_IN_DAY = 24L
    val DAYS_IN_MONTH = 30L

    val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
    val now = Clock.System.now()
    val duration = now - inputInstant

    return when {
        duration.inWholeSeconds < SECONDS_IN_MINUTE -> "Now"
        duration.inWholeMinutes < MINUTES_IN_HOUR -> "${duration.inWholeMinutes}m"
        duration.inWholeHours < HOURS_IN_DAY -> "${duration.inWholeHours}h"
        duration.inWholeDays < DAYS_IN_MONTH -> "${duration.inWholeDays}d"
        else -> {
            val localDateTime = inputInstant.toLocalDateTime(TimeZone.currentSystemDefault())
            val month =
                localDateTime.month.name.take(3).lowercase().replaceFirstChar { it.uppercase() }
            val day = localDateTime.dayOfMonth.toString().padStart(2, '0')
            "$month $day"
        }
    }
}

fun Timestamp.formatAsGroupHeader(): String {
    val timeZone = TimeZone.currentSystemDefault()
    val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
    val inputDate = inputInstant.toLocalDateTime(timeZone).date

    val now = Clock.System.now()
    val today = now.toLocalDateTime(timeZone).date
    val yesterday = today.minus(1, DateTimeUnit.DAY)

    return when (inputDate) {
        today -> "Today"
        yesterday -> "Yesterday"
        else -> {
            val day = inputDate.day.toString().padStart(2, '0')
            val month = inputDate.month.number.toString().padStart(2, '0')
            val year = inputDate.year.toString().takeLast(2)
            "$day/$month/$year"
        }
    }
}

fun Timestamp.humanReadable(): String {
    val timeZone = TimeZone.currentSystemDefault()
    val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
    val inputDateTime = inputInstant.toLocalDateTime(timeZone)
    val inputDate = inputDateTime.date

    val now = Clock.System.now()
    val today = now.toLocalDateTime(timeZone).date
    val yesterday = today.minus(1, DateTimeUnit.DAY)

    val hour = inputDateTime.hour
    val minute = inputDateTime.minute.toString().padStart(2, '0')
    val amPm = if (hour < 12) "AM" else "PM"
    val hour12 = when {
        hour == 0 -> 12
        hour > 12 -> hour - 12
        else -> hour
    }
    val timeFormat = "$hour12:$minute $amPm"

    return when (inputDate) {
        today -> "Today at $timeFormat"
        yesterday -> "Yesterday at $timeFormat"
        else -> {
            val day = inputDateTime.day.toString().padStart(2, '0')
            val month = inputDateTime.month.number.toString().padStart(2, '0')
            val year = inputDateTime.year.toString().takeLast(2)
            "$day/$month/$year, $timeFormat"
        }
    }
}
