package ru.forbion.f7cloud.feature.calendar

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
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
import ru.forbion.f7cloud.core.network.applyOcsJson
import ru.forbion.f7cloud.core.network.isOcsSuccess
import ru.forbion.f7cloud.core.network.ocsMeta
import ru.forbion.f7cloud.core.network.parseJsonObject

data class CalendarAttendeeSuggestion(
    val name: String,
    val email: String,
    val type: String,
)

data class CalendarLocationSuggestion(
    val name: String,
    val address: String,
)

data class CalendarUserSettings(
    val timezone: String = "automatic",
    val showWeekends: Boolean = true,
    val showWeekNumbers: Boolean = false,
    val defaultReminderMinutes: Int = 15,
    val showTasks: Boolean = true,
    val tasksSidebar: Boolean = true,
)

class CalendarApiClient {
    fun searchAttendees(session: AuthSession, query: String): List<CalendarAttendeeSuggestion> {
        if (query.trim().length < 2) return emptyList()
        val client = authedClient(session)
        val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/calendar/v1/autocompletion/attendee"
        val body = JSONObject().put("search", query.trim()).toString()
        val request = Request.Builder()
            .url(url)
            .post(body.toRequestBody("application/json; charset=utf-8".toMediaType()))
            .applyOcsJson()
            .build()
        client.newCall(request).execute().use { response ->
            val json = parseJsonObject(response.body?.string().orEmpty(), "autocomplete attendee")
            val data = json.optJSONArray("data") ?: json.optJSONObject("ocs")?.optJSONArray("data") ?: return emptyList()
            return buildList {
                for (i in 0 until data.length()) {
                    val item = data.optJSONObject(i) ?: continue
                    val emails = item.optJSONArray("emails")
                    val email = emails?.optString(0).orEmpty()
                    if (email.isBlank()) continue
                    add(
                        CalendarAttendeeSuggestion(
                            name = item.optString("name", email),
                            email = email,
                            type = item.optString("type", "individual"),
                        ),
                    )
                }
            }
        }
    }

    fun searchLocations(session: AuthSession, query: String): List<CalendarLocationSuggestion> {
        if (query.trim().length < 2) return emptyList()
        val client = authedClient(session)
        val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/calendar/v1/autocompletion/location?search=${java.net.URLEncoder.encode(query.trim(), Charsets.UTF_8.name())}"
        val request = Request.Builder()
            .url(url)
            .get()
            .applyOcsJson()
            .build()
        client.newCall(request).execute().use { response ->
            val json = parseJsonObject(response.body?.string().orEmpty(), "autocomplete location")
            val data = json.optJSONArray("data") ?: return emptyList()
            return buildList {
                for (i in 0 until data.length()) {
                    val item = data.optJSONObject(i) ?: continue
                    add(
                        CalendarLocationSuggestion(
                            name = item.optString("name", ""),
                            address = item.optString("address", item.optString("label", "")),
                        ),
                    )
                }
            }
        }
    }

    fun setConfig(session: AuthSession, key: String, value: String) {
        val client = authedClient(session)
        val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/calendar/v1/config/$key"
        val body = JSONObject().put("value", value).toString()
        val request = Request.Builder()
            .url(url)
            .post(body.toRequestBody("application/json; charset=utf-8".toMediaType()))
            .applyOcsJson()
            .build()
        client.newCall(request).execute().use { response ->
            if (!response.isSuccessful) error("Calendar config failed HTTP ${response.code}")
        }
    }

    fun createTalkRoom(session: AuthSession, roomName: String): String {
        val client = authedClient(session)
        val url = "${session.serverUrl.trimEnd('/')}/ocs/v2.php/apps/spreed/api/v4/room?format=json"
        val payload = JSONObject()
            .put("roomType", 2)
            .put("roomName", roomName.trim().ifBlank { "Встреча" })
            .put("readOnly", 0)
            .put("listable", 0)
            .toString()
        val request = Request.Builder()
            .url(url)
            .post(payload.toRequestBody("application/json; charset=utf-8".toMediaType()))
            .applyOcsJson()
            .build()
        client.newCall(request).execute().use { response ->
            val json = parseJsonObject(response.body?.string().orEmpty(), "create talk room")
            val meta = json.ocsMeta()
            if (!isOcsSuccess(meta)) error("Не удалось создать комнату Talk")
            val token = json.optJSONObject("ocs")?.optJSONObject("data")?.optString("token").orEmpty()
            if (token.isBlank()) error("Не удалось создать комнату Talk")
            return "${session.serverUrl.trimEnd('/')}/call/$token"
        }
    }

    fun addTalkParticipants(session: AuthSession, callUrl: String, emails: List<String>) {
        val token = callUrl.substringAfter("/call/").substringBefore('?').substringBefore('#')
        if (token.isBlank()) return
        val client = authedClient(session)
        emails.forEach { email ->
            val url = "${session.serverUrl.trimEnd('/')}/ocs/v2.php/apps/spreed/api/v4/room/$token/participants?format=json"
            val payload = JSONObject()
                .put("newParticipant", email)
                .put("source", "emails")
                .toString()
            val request = Request.Builder()
                .url(url)
                .post(payload.toRequestBody("application/json; charset=utf-8".toMediaType()))
                .applyOcsJson()
                .build()
            runCatching {
                client.newCall(request).execute().use { /* best effort */ }
            }
        }
    }

    private fun authedClient(session: AuthSession) = NetworkFactory.newAuthedClient(
        session.username,
        session.appPassword,
        session.trustAllCerts,
    )
}
