package bou.amine.apps.readerforselfossv2.rest

import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.cache.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json

class SelfossApi(private val appSettingsService: AppSettingsService) {

    var client = createHttpClient()

    private fun createHttpClient(): HttpClient {
        return HttpClient {
            install(ContentNegotiation) {
                install(HttpCache)
                json(Json {
                    prettyPrint = true
                    isLenient = true
                    ignoreUnknownKeys = true
                })
            }
            install(Logging) {
                logger = object : Logger {
                    override fun log(message: String) {
                        appSettingsService.logApiCalls(message)
                    }
                }
                level = LogLevel.INFO
            }
            install(HttpTimeout) {
                requestTimeoutMillis = appSettingsService.getApiTimeout()
            }
            /* TODO: Auth as basic
            if (apiDetailsService.getUserName().isNotEmpty() && apiDetailsService.getPassword().isNotEmpty()) {

                install(Auth) {
                    basic {
                        credentials {
                            BasicAuthCredentials(username = apiDetailsService.getUserName(), password = apiDetailsService.getPassword())
                        }
                        sendWithoutRequest {
                            true
                        }
                    }
                }
            }*/
            expectSuccess = false
        }
    }

    fun url(path: String) =
        "${appSettingsService.getBaseUrl()}$path"

    fun refreshLoginInformation() {
        appSettingsService.refreshApiSettings()
        client = createHttpClient()
    }

    suspend fun login(): SelfossModel.SuccessResponse =
        maybeResponse(client.get(url("/login")) {
            parameter("username", appSettingsService.getUserName())
            parameter("password", appSettingsService.getPassword())
        })

    suspend fun getItems(
        type: String,
        offset: Int,
        tag: String?,
        source: Long?,
        search: String?,
        updatedSince: String?,
        items: Int? = null
    ): SelfossModel.StatusAndData<List<SelfossModel.Item>> =
        bodyOrFailure(client.get(url("/items")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
                parameter("type", type)
                parameter("tag", tag)
                parameter("source", source)
                parameter("search", search)
                parameter("updatedsince", updatedSince)
                parameter("items", items ?: appSettingsService.getItemsNumber())
                parameter("offset", offset)
            })

    suspend fun stats(): SelfossModel.StatusAndData<SelfossModel.Stats> =
        bodyOrFailure(client.get(url("/stats")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
            })

    suspend fun tags(): SelfossModel.StatusAndData<List<SelfossModel.Tag>> =
        bodyOrFailure(client.get(url("/tags")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
            })

    suspend fun update(): SelfossModel.StatusAndData<String> =
        bodyOrFailure(client.get(url("/update")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
            })

    suspend fun spouts(): SelfossModel.StatusAndData<Map<String, SelfossModel.Spout>> =
        bodyOrFailure(client.get(url("/sources/spouts")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
            })

    suspend fun sources(): SelfossModel.StatusAndData<ArrayList<SelfossModel.Source>> =
        bodyOrFailure(client.get(url("/sources/list")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
            })

    suspend fun version(): SelfossModel.StatusAndData<SelfossModel.ApiVersion> =
        bodyOrFailure(client.get(url("/api/about")))

    suspend fun markAsRead(id: String): SelfossModel.SuccessResponse =
        maybeResponse(client.post(url("/mark/$id")) {
            parameter("username", appSettingsService.getUserName())
            parameter("password", appSettingsService.getPassword())
        })

    suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse =
        maybeResponse(client.post(url("/unmark/$id")) {
            parameter("username", appSettingsService.getUserName())
            parameter("password", appSettingsService.getPassword())
        })

    suspend fun starr(id: String): SelfossModel.SuccessResponse =
        maybeResponse(client.post(url("/starr/$id")) {
            parameter("username", appSettingsService.getUserName())
            parameter("password", appSettingsService.getPassword())
        })

    suspend fun unstarr(id: String): SelfossModel.SuccessResponse =
        maybeResponse(client.post(url("/unstarr/$id")) {
            parameter("username", appSettingsService.getUserName())
            parameter("password", appSettingsService.getPassword())
        })

    suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse =
        maybeResponse(client.submitForm(
            url = url("/mark"),
            formParameters = Parameters.build {
                append("username", appSettingsService.getUserName())
                append("password", appSettingsService.getPassword())
                ids.map { append("ids[]", it) }
            }
        ))

    suspend fun createSourceForVersion(
        title: String,
        url: String,
        spout: String,
        tags: String,
        filter: String,
        version: Int
    ): SelfossModel.SuccessResponse =
        maybeResponse(
            if (version > 1) {
                createSource2(title, url, spout, tags, filter)
            } else {
                createSource(title, url, spout, tags, filter)
            }
        )

    suspend fun createSource(
        title: String,
        url: String,
        spout: String,
        tags: String,
        filter: String
    ): HttpResponse =
        client.submitForm(
            url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
            formParameters = Parameters.build {
                append("title", title)
                append("url", url)
                append("spout", spout)
                append("tags", tags)
                append("filter", filter)
            }
        )

    suspend fun createSource2(
        title: String,
        url: String,
        spout: String,
        tags: String,
        filter: String
    ): HttpResponse =
        client.submitForm(
            url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
            formParameters = Parameters.build {
                append("title", title)
                append("url", url)
                append("spout", spout)
                append("tags[]", tags)
                append("filter", filter)
            }
        )

    suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse =
        maybeResponse(client.delete(url("/source/$id")) {
                parameter("username", appSettingsService.getUserName())
                parameter("password", appSettingsService.getPassword())
            })

    suspend fun maybeResponse(r: HttpResponse): SelfossModel.SuccessResponse {
        return if (r.status.isSuccess()) {
            r.body()
        } else {
            SelfossModel.SuccessResponse(false)
        }
    }

    suspend inline fun <reified T> bodyOrFailure(r: HttpResponse): SelfossModel.StatusAndData<T> {
        return if (r.status.isSuccess()) {
            SelfossModel.StatusAndData.succes(r.body())
        } else {
            SelfossModel.StatusAndData.error()
        }
    }
}