package remote

import ILIAS_CLIENT_ID
import ILIAS_DOMAIN
import ILIAS_PASSWORD
import ILIAS_USER
import okhttp3.*
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.http.*
import java.util.regex.Pattern

object Ilias {
    private val ilias = Retrofit.Builder()
            .baseUrl(ILIAS_DOMAIN)
            .addConverterFactory(ScalarsConverterFactory.create())
            .client(OkHttpClient.Builder()
                    .cookieJar(CookieStore()).build())
            .build().create(IliasAPI::class.java)

    fun listIliasResources(type: String, pageId: String): List<IliasResource> {
        var response = ilias.listPage(pageId, pageId).execute()
        if (response.body()?.contains("Übungsblätter") != true) {
            ilias.login(ILIAS_USER, ILIAS_PASSWORD, "Anmelden").execute()
            response = ilias.listPage(pageId, pageId).execute()
        }

        val matcher = Pattern.compile("""<div class="form-group">\s*<div[^>]*>(.*\.pdf)</div>\s*<div[^>]*>\s*<a href="([^"]*)"[^>]*>Download</a>\s*</div>\s*</div>""")
                .matcher(response.body())
        val iliasResources = mutableListOf<IliasResource>()
        while (matcher.find()) {
            iliasResources.add(IliasResource(type, matcher.group(1),
                    matcher.group(2).replace("&amp;", "&")))
        }

        return iliasResources
    }

    fun listIliasResourcesGoto(type: String, url: String): List<IliasResource> {
        var response = ilias.download(url).execute().body()?.string()
        if (response?.contains("Inhalt") != true) {
            ilias.login(ILIAS_USER, ILIAS_PASSWORD, "Anmelden").execute()
            response = ilias.download(url).execute().body()?.string()
        }

        val matcher = Pattern.compile("""<h4 class="il_ContainerItemTitle"><a href="([^"]+)"[^>]*>([^<]+)</a>.*?</a>""")
                .matcher(response ?: "")

        val iliasResources = mutableListOf<IliasResource>()
        while (matcher.find()) {
            val nameShortend = matcher.group(2).replace("Übungsblatt", "Blatt")
            iliasResources.add(IliasResource(type, "$nameShortend.pdf",
                    matcher.group(1).replace("&amp;", "&")))
        }

        return iliasResources
    }


    fun listWebResources(baseName: String, baseUrl: String): List<IliasResource> {
        val matcher = Pattern.compile("""<a href="(uebung/g[0-9]+\.pdf)">([^<]+)</a>""")
                .matcher(ilias.download(baseUrl).execute().body()?.string())

        val iliasResources = mutableListOf<IliasResource>()
        while (matcher.find()) {
            iliasResources.add(IliasResource(baseName, "${matcher.group(2)}.pdf",
                    baseUrl + matcher.group(1)))
        }

        return iliasResources.toList()
    }

    fun download(url: String): ByteArray {
        var response = ilias.download(url).execute()
        if (response.raw().request().url().encodedPath().contains("login")) {
            ilias.login(ILIAS_USER, ILIAS_PASSWORD, "Anmelden").execute()
            response = ilias.download(url).execute()
        }

        return response.body()!!.bytes()
    }


    private interface IliasAPI {
        @FormUrlEncoded
        @POST("ilias.php?client_id=$ILIAS_CLIENT_ID&cmd=post&baseClass=ilStartUpGUI")
        fun login(@Field("username") username: String,
                  @Field("password") password: String,
                  @Field("cmd[doStandardAuthentication]") authentication: String): Call<String>

        @GET("ilias.php?cmd=showOverview&baseClass=ilexercisehandlergui")
        fun listPage(@Query("ref_id") ref_id: String,
                     @Query("target") target: String): Call<String>

        @GET
        fun download(@Url url: String): Call<ResponseBody>
    }

    private data class CookieStore(private var cookies: List<Cookie> = emptyList()) : CookieJar {
        override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
            this.cookies = cookies
        }

        override fun loadForRequest(url: HttpUrl): List<Cookie> {
            return cookies
        }
    }
}
