package com.nisemoe.nise.integrations

import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import org.springframework.beans.factory.InitializingBean
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.security.SecureRandom
import java.util.*

@Service
class MetabaseService : InitializingBean {

    @Value("\${METABASE_API_KEY:nil}")
    private lateinit var metabaseApiKey: String

    @Value("\${METABASE_URL:https://neko.nise.moe}")
    private lateinit var metabaseUrl: String

    override fun afterPropertiesSet() {
        if (this.metabaseApiKey.isBlank())
            throw IllegalArgumentException("METABASE_API_KEY is not set")
    }

    private val httpClient: HttpClient = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_1_1)
        .build()

    data class CreateNewUserResponse(
        val email: String,
        val password: String
    )

    private fun getUserDataFromUsername(name: String): UserData? {
        val email = usernameToEmail(name)

        val request = HttpRequest.newBuilder()
            .uri(URI.create("$metabaseUrl/api/user/?query=$email"))
            .GET()
            .header("Content-type", "application/json")
            .header("x-api-key", this.metabaseApiKey)
            .build()

        val response = HttpClient.newBuilder().build().send(request, HttpResponse.BodyHandlers.ofString())

        return if (response.statusCode() == 200) {
            val json = Json { ignoreUnknownKeys = true }
            val responseBody = json.decodeFromString<GetUserResponse>(response.body())
            responseBody.data.firstOrNull { it.email == email }
        } else {
            null
        }
    }

    private fun generateRandomPassword(length: Int = 16): String {
        val charPool = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        val secureRandom = SecureRandom()
        return (1..length)
            .map { secureRandom.nextInt(charPool.length) }
            .map(charPool::get)
            .joinToString("")
    }

    fun createNewUser(name: String): CreateNewUserResponse {
        val email = usernameToEmail(name)
        val password = generateRandomPassword()

        val requestBody = buildJsonObject {
            put("email", email)
            put("password", password)
        }

        val request = HttpRequest.newBuilder()
            .uri(URI.create("$metabaseUrl/api/user/"))
            .POST(HttpRequest.BodyPublishers.ofString(Json.encodeToString(requestBody)))
            .header("Content-type", "application/json")
            .header("x-api-key", this.metabaseApiKey)
            .build()

        val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())

        if (response.statusCode() == 200) {
            val responseBody = Json.decodeFromString<Map<String, Any>>(response.body())
            if (responseBody["email"] == email &&
                responseBody["is_active"] == true &&
                responseBody["is_superuser"] == false
            ) {
                return CreateNewUserResponse(
                    email = email,
                    password = password
                )
            } else {
                throw RuntimeException("Failed to validate user creation response: ${response.body()}")
            }
        } else if (response.statusCode() == 400) {
            throw IllegalArgumentException("User already exists.")
        } else {
            throw RuntimeException("Failed to create user: ${response.body()}")
        }
    }

    private fun usernameToEmail(name: String): String {
        val cleanedName = name.replace(" ", "_").lowercase(Locale.getDefault())
        val email = "$cleanedName@nise.moe"
        return email
    }

    @Serializable
    data class GetUserResponse(
        val data: List<UserData>
    )

    @Serializable
    data class UserData(
        val id: Int,
        val email: String
    )

    /**
     * Fetches a user from Metabase by their username.
     * @param name The username to fetch.
     * @return The user's email if found, null otherwise.
     */
    fun getUser(name: String): String? {
        val user = getUserDataFromUsername(name)
        return user?.email
    }

    fun resetPassword(name: String): CreateNewUserResponse {
        val user = getUserDataFromUsername(name) ?: throw IllegalArgumentException("User not found.")
        val password = generateRandomPassword()

        val requestBody = buildJsonObject {
            put("password", password)
        }

        val request = HttpRequest.newBuilder()
            .uri(URI.create("$metabaseUrl/api/user/${user.id}/password"))
            .PUT(HttpRequest.BodyPublishers.ofString(Json.encodeToString(requestBody)))
            .header("Content-type", "application/json")
            .header("x-api-key", this.metabaseApiKey)
            .build()

        val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())

        if (response.statusCode() == 204) {
            return CreateNewUserResponse(
                email = user.email,
                password = password
            )
        } else {
            throw RuntimeException("Failed to reset password: ${response.body()}")
        }
    }

    
}
