package ru.myitschool.work.data.source

import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import ru.myitschool.work.core.Constants
import ru.myitschool.work.data.dto.AuthRequestDto
import ru.myitschool.work.data.dto.AuthResponseDto
import ru.myitschool.work.data.dto.PlaceDto
import ru.myitschool.work.data.dto.BookRequestDto
import ru.myitschool.work.data.dto.UserDto
import ru.myitschool.work.data.repo.AuthRepository

object NetworkDataSource {
    private val client by lazy {
        HttpClient(CIO) {
            install(ContentNegotiation) {
                json(
                    Json {
                        isLenient = true
                        ignoreUnknownKeys = true
                        explicitNulls = true
                        encodeDefaults = true
                    }
                )
            }
        }
    }

    suspend fun checkAuth(data: AuthRequestDto): Result<AuthResponseDto> = withContext(Dispatchers.IO) {
        return@withContext runCatching {
            val response = client.post(getUrl(Constants.AUTH_URL)) {
                contentType(ContentType.Application.Json)
                setBody(data)
            }
            when (response.status) {
                HttpStatusCode.OK -> response.body<AuthResponseDto>()
                else -> error(response.bodyAsText())
            }
        }
    }

    suspend fun getInfo(token: String): Result<UserDto> = withContext(Dispatchers.IO) {
        return@withContext runCatching {
            println("!!!!!!!!!!!!!! getInfo")
            val response = client.get(getUrl(Constants.INFO_URL)) {
                headers {
                    append("Authorization", "Bearer $token")
                }
            }
            if (response.status == HttpStatusCode.OK) {
                println("!!!!!!!!!!!!!! getInfo OK ${response.bodyAsText()}")
                response.body<UserDto>()
            } else {
                if (response.status == HttpStatusCode.Unauthorized) {
                    AuthRepository.logout()
                }
                println("!!!!!!!!!!!!!! getInfo ERROR ${response.bodyAsText()}")
                error(response.bodyAsText())
            }
        }
    }

    suspend fun getBooking(token: String): Result<Map<String, List<PlaceDto>>?> = withContext(Dispatchers.IO) {
        return@withContext runCatching {
            val response = client.get(getUrl(Constants.BOOKING_URL)) {
                headers {
                    append("Authorization", "Bearer $token")
                }
            }
            if (response.status == HttpStatusCode.OK) {
                response.body<Map<String, List<PlaceDto>>>()
            } else {
                if (response.status == HttpStatusCode.Unauthorized) {
                    AuthRepository.logout()
                }
                error(response.bodyAsText())
            }
        }
    }

    suspend fun addBook(token: String, data: BookRequestDto): Result<Boolean> = withContext(Dispatchers.IO) {
        return@withContext runCatching {
            val response = client.post(getUrl(Constants.BOOK_URL)) {
                contentType(ContentType.Application.Json)
                setBody(data)
                headers {
                    append("Authorization", "Bearer $token")
                }
            }
            when (response.status) {
                HttpStatusCode.Created -> true
                HttpStatusCode.Conflict -> false
                else -> {
                    if (response.status == HttpStatusCode.Unauthorized) {
                        AuthRepository.logout()
                    }
                    error(response.bodyAsText())
                }
            }
        }
    }

    private fun getUrl(targetUrl: String) = "${Constants.HOST}/api$targetUrl"
}