package ru.forbion.f7cloud.feature.deck

import okhttp3.Request
import org.json.JSONArray
import org.json.JSONObject
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.network.NetworkFactory
import ru.forbion.f7cloud.core.network.UnauthorizedException

class DeckRepository {
    private fun apiBase(session: AuthSession): String {
        return "${session.serverUrl.trimEnd('/')}/index.php/apps/deck/api/v1.0"
    }

    suspend fun loadBoards(session: AuthSession): List<DeckBoard> {
        val client = NetworkFactory.newAuthedClient(session.username, session.appPassword, session.trustAllCerts)
        val json = getJson(client, "${apiBase(session)}/boards")
        val array = when (json) {
            is JSONArray -> json
            is JSONObject -> JSONArray().put(json)
            else -> JSONArray()
        }
        val out = mutableListOf<DeckBoard>()
        for (i in 0 until array.length()) {
            val board = array.optJSONObject(i) ?: continue
            val id = board.optInt("id", 0)
            val title = board.optString("title")
            if (id > 0 && title.isNotBlank()) {
                out += DeckBoard(id = id, title = title, color = board.optString("color"))
            }
        }
        return out
    }

    suspend fun loadCard(session: AuthSession, cardId: Int): DeckCardDetail {
        val client = NetworkFactory.newAuthedClient(session.username, session.appPassword, session.trustAllCerts)
        val json = getJson(client, "${apiBase(session)}/cards/$cardId") as JSONObject
        val boardId = json.optInt("boardId", json.optInt("board_id", 0))
        if (boardId <= 0) error("Карточка не найдена")
        return DeckCardDetail(
            cardId = cardId,
            boardId = boardId,
            title = json.optString("title"),
        )
    }

    suspend fun loadBoardDetail(session: AuthSession, boardId: Int): DeckBoardDetail {
        val client = NetworkFactory.newAuthedClient(session.username, session.appPassword, session.trustAllCerts)
        val stacksJson = getJson(client, "${apiBase(session)}/boards/$boardId/stacks")
        val stacksArray = when (stacksJson) {
            is JSONArray -> stacksJson
            else -> JSONArray()
        }
        val stacks = mutableListOf<DeckStack>()
        for (i in 0 until stacksArray.length()) {
            val stack = stacksArray.optJSONObject(i) ?: continue
            val stackId = stack.optInt("id", 0)
            val title = stack.optString("title")
            val cards = mutableListOf<DeckCard>()
            val cardsArray = stack.optJSONArray("cards") ?: JSONArray()
            for (c in 0 until cardsArray.length()) {
                val card = cardsArray.optJSONObject(c) ?: continue
                val cardTitle = card.optString("title")
                if (cardTitle.isNotBlank()) {
                    cards += DeckCard(
                        id = card.optInt("id", 0),
                        title = cardTitle,
                        done = card.has("done") && !card.isNull("done"),
                    )
                }
            }
            if (stackId > 0) {
                stacks += DeckStack(id = stackId, title = title.ifBlank { "Stack" }, cards = cards)
            }
        }
        return DeckBoardDetail(boardId = boardId, stacks = stacks)
    }

    private fun getJson(client: okhttp3.OkHttpClient, url: String): Any {
        val request = Request.Builder().url(url).build()
        client.newCall(request).execute().use { response ->
            if (!response.isSuccessful || response.body == null) {
                error("Deck API HTTP ${response.code}")
            }
            val body = response.body!!.string().trim()
            if (body.startsWith("[")) return JSONArray(body)
            if (body.startsWith("{")) return JSONObject(body)
            return JSONArray()
        }
    }
}

data class DeckBoard(
    val id: Int,
    val title: String,
    val color: String,
)

data class DeckStack(
    val id: Int,
    val title: String,
    val cards: List<DeckCard>,
)

data class DeckCard(
    val id: Int,
    val title: String,
    val done: Boolean,
)

data class DeckBoardDetail(
    val boardId: Int,
    val stacks: List<DeckStack>,
)

data class DeckCardDetail(
    val cardId: Int,
    val boardId: Int,
    val title: String,
)
