package com.peterino.blastoise

import org.json.JSONArray
import org.json.JSONObject

data class Track(
    val id: String,
    val filename: String,
    val title: String,
    val duration: Double,
)

data class ChannelInfo(
    val id: String,
    val name: String,
    val description: String,
    val listenerCount: Int,
    val isDefault: Boolean,
    val trackCount: Int = 0,
    val listeners: List<String> = emptyList(),
)

data class ChannelState(
    val track: Track?,
    val currentTimestamp: Double,
    val channelName: String,
    val channelId: String,
    val paused: Boolean,
    val queue: List<Track>?,
    val currentIndex: Int,
    val playbackMode: String,
    val listenerCount: Int,
    val listeners: List<String>,
)

data class Playlist(
    val id: String,
    val name: String,
    val description: String,
    val ownerId: Int,
    val ownerName: String,
    val isPublic: Boolean,
    val shareToken: String?,
    val trackIds: List<String>,
)

data class PlaylistBundle(
    val mine: List<Playlist>,
    val shared: List<Playlist>,
)

data class Permission(
    val resourceType: String,
    val resourceId: String?,
    val permission: String,
)

data class UserSession(
    val id: Int,
    val username: String,
    val isAdmin: Boolean,
    val isGuest: Boolean,
    val permissions: List<Permission> = emptyList(),
) {
    val isSignedIn: Boolean
        get() = !isGuest
}

fun JSONObject.toTrack(): Track {
    return Track(
        id = optString("id", optString("filename")),
        filename = optString("filename"),
        title = optString("title", optString("filename", "Unknown")),
        duration = optDouble("duration", 0.0),
    )
}

fun JSONObject.toChannelInfo(): ChannelInfo {
    return ChannelInfo(
        id = getString("id"),
        name = optString("name", "Channel"),
        description = optString("description", ""),
        listenerCount = optInt("listenerCount", 0),
        isDefault = optBoolean("isDefault", false),
        trackCount = optInt("trackCount", 0),
        listeners = optJSONArray("listeners").toStringList(),
    )
}

fun JSONObject.toChannelState(): ChannelState {
    val trackJson = optJSONObject("track")
    val queueJson = optJSONArray("queue")
    val queue = if (queueJson == null) {
        null
    } else {
        (0 until queueJson.length()).map { queueJson.getJSONObject(it).toTrack() }
    }

    return ChannelState(
        track = trackJson?.toTrack(),
        currentTimestamp = optDouble("currentTimestamp", 0.0),
        channelName = optString("channelName", ""),
        channelId = optString("channelId", ""),
        paused = optBoolean("paused", true),
        queue = queue,
        currentIndex = optInt("currentIndex", 0),
        playbackMode = optString("playbackMode", "repeat-all"),
        listenerCount = optInt("listenerCount", 0),
        listeners = optJSONArray("listeners").toStringList(),
    )
}

fun JSONObject.toPermission(): Permission {
    return Permission(
        resourceType = optString("resource_type", optString("resourceType", "")),
        resourceId = when {
            isNull("resource_id") -> null
            has("resource_id") -> optString("resource_id")
            isNull("resourceId") -> null
            else -> optString("resourceId")
        },
        permission = optString("permission", ""),
    )
}

fun JSONObject.toUserSession(): UserSession {
    val user = optJSONObject("user") ?: this
    return UserSession(
        id = user.optInt("id", 0),
        username = user.optString("username", "guest"),
        isAdmin = user.optBoolean("isAdmin", user.optBoolean("is_admin", false)),
        isGuest = user.optBoolean("isGuest", user.optBoolean("is_guest", false)),
        permissions = optJSONArray("permissions").toPermissions(),
    )
}

fun JSONObject.toPlaylist(): Playlist {
    return Playlist(
        id = getString("id"),
        name = optString("name", "Playlist"),
        description = optString("description", ""),
        ownerId = optInt("ownerId", 0),
        ownerName = optString("ownerName", ""),
        isPublic = optBoolean("isPublic", false),
        shareToken = if (isNull("shareToken")) null else getString("shareToken"),
        trackIds = optJSONArray("trackIds").toStringList(),
    )
}

fun JSONArray.toChannels(): List<ChannelInfo> {
    return (0 until length()).map { getJSONObject(it).toChannelInfo() }
}

fun JSONArray.toTracks(): List<Track> {
    return (0 until length()).map { getJSONObject(it).toTrack() }
}

fun JSONArray?.toPlaylists(): List<Playlist> {
    if (this == null) return emptyList()
    return (0 until length()).map { getJSONObject(it).toPlaylist() }
}

fun JSONArray?.toStringList(): List<String> {
    if (this == null) return emptyList()
    return (0 until length()).mapNotNull { optString(it).takeIf(String::isNotBlank) }
}

fun JSONArray?.toPermissions(): List<Permission> {
    if (this == null) return emptyList()
    return (0 until length()).map { getJSONObject(it).toPermission() }
}
