package de.sebse.fuplanner2.whiteboard

import android.content.Context
import com.android.volley.VolleyError
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import de.sebse.fuplanner2.auth.FUAuthModule
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.database.Lecturer
import de.sebse.fuplanner2.database.User
import de.sebse.fuplanner2.network.Requester
import de.sebse.fuplanner2.utils.UpdateResult
import de.sebse.fuplanner2.utils.console
import de.sebse.fuplanner2.utils.pmap
import de.sebse.fuplanner2.utils.updateResultOf
import de.sebse.fuplanner2.worker.FetchResourceErrorType
import de.sebse.fuplanner2.worker.FetchResourceException



suspend fun Whiteboard.getCourseByLocationRef(stored: List<Course>, requester: Requester, user: User, locationRef: String): Course {
    val course = stored.find { it.internalId == locationRef }?.apply {
        if (lastRefreshed > System.currentTimeMillis() - FUAuthModule.REFRESH_FREQUENCY_COURSES_MILLIS)
            return this
    }
    return getCourseByCourse(requester, user, locationRef, course)
}

suspend fun Whiteboard.getCourseByCourse(requester: Requester, user: User, locationRef: String, course: Course?): Course {
    val jsonSite = requester
        .get(
            FETCH_COURSE_DETAILS.format(locationRef),
            getCookies(user)
        )
        .let { Parser.default().parse(StringBuilder(it.body)) as JsonObject }
    val uid = course?.uid
    val moduleType = course?.moduleType ?: MODULE_TYPE
    val (isSummerSemester, year) = jsonSite.obj("props")?.string("term_eid")?.let semester@{termEid ->
        val type: String? = Regex("^(SS|WS) ").find(termEid)?.groupValues?.getOrNull(1)
        val year: String? = Regex("^(SS|WS) ([0-9]{2})").find(termEid)?.groupValues?.getOrNull(2)
        val isSS = type == "SS"
        val yearInt = if (type != null && year != null) year.toIntOrNull(10) else null
        return@semester Pair(isSS, yearInt)
    } ?: Pair(false, null)
    val lecturers = jsonSite.obj("props")?.string("kvv_lecturers")
        ?.split("#")
        ?.mapNotNull { lecString ->
            return@mapNotNull Regex("(.*?)\\|(.*?)\\|(.*?)\\|\\|(.*)")
                .matchEntire(lecString)
                ?.groupValues?.let lecturer@{ match ->
                    return@lecturer Lecturer(match[1], match[2], match[3], match[4] == "true")
                }
        } ?: listOf()
    val lvNumbers = jsonSite
        .obj("props")?.string("kvv_lvnumbers")
        ?.split(" + ")
        ?.toHashSet() ?: hashSetOf()
    @Suppress("SpellCheckingInspection")
    return Course(
        uid,
        user.uid!!,
        System.currentTimeMillis(),
        isSummerSemester,
        year,
        lvNumbers,
        jsonSite.string("entityTitle") ?: "",
        jsonSite.obj("props")?.string("kvv_coursetype") ?: "Sonstiges",
        jsonSite.string("description") ?: "",
        locationRef,
        moduleType,
        lecturers
    )
}

suspend fun Whiteboard.getCourses(ctx: Context, database: AppDatabase, user: User): UpdateResult<Course> {
    if (!isAvailable(user)) return UpdateResult()
    val userId = user.uid ?: return UpdateResult()
    val requester = Requester(ctx)
    val stored = database.courseDao().getAllByType(userId, MODULE_TYPE)

    try {
        val data = requester.get(
            FETCH_COURSE_MEMBERSHIP,
            getCookies(user)
        )
        val json = Parser.default().parse(StringBuilder(data.body)) as JsonObject
        val new = json.array<JsonObject>("membership_collection")?.pmap { obj ->
            val locationRef = obj.string("locationReference") ?: ""
            getCourseByLocationRef(stored, requester, user, locationRef = locationRef)
        } ?: listOf()
        val result = updateResultOf(stored, new)
        database.courseDao().run {
            delete(result.removed)
            upsert(result.values)
        }
        return result
    } catch (e: VolleyError) {
        val statusCode = e.networkResponse.statusCode
        if (statusCode == 401)
            throw FetchResourceException(FetchResourceErrorType.ERR_AUTHORIZATION, statusCode)
        throw FetchResourceException(FetchResourceErrorType.ERR_NETWORK_ERROR, statusCode)
    }
}

suspend fun Whiteboard.getCourse(ctx: Context, database: AppDatabase, user: User, courseId: Long): UpdateResult<Course> {
    if (!isAvailable(user)) return UpdateResult()
    val requester = Requester(ctx)

    try {
        database.courseDao().run {
            val old = getCourseById2(courseId)
            if (old.moduleType == MODULE_TYPE) {
                getCourseByCourse(requester, user, old.internalId, old).also {
                    upsert(it)
                    return updateResultOf(listOf(old), listOf(it))
                }
            }
            return UpdateResult()
        }
    } catch (e: VolleyError) {
        val statusCode = e.networkResponse.statusCode
        if (statusCode == 401)
            throw FetchResourceException(FetchResourceErrorType.ERR_AUTHORIZATION, statusCode)
        throw FetchResourceException(FetchResourceErrorType.ERR_NETWORK_ERROR, statusCode)
    }
}