package com.optix.android.tools.osint

import com.optix.android.domain.model.OsintResult
import com.optix.android.domain.model.ParamType
import com.optix.android.domain.model.ToolCategory
import com.optix.android.domain.model.ToolEvent
import com.optix.android.domain.model.ToolMetadata
import com.optix.android.domain.model.ToolParam
import com.optix.android.tools.registry.BaseTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Credentials
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject

/**
 * DeHashed breach intelligence search.
 *
 * Queries the DeHashed API (https://api.dehashed.com) using HTTP Basic auth
 * where the username is the account email and the password is the API key.
 * Returns the number of matching leaked credentials/entries.
 */
class DehashedTool(private val client: OkHttpClient) : BaseTool() {

    override val metadata = ToolMetadata(
        name = "dehashed_search",
        displayName = "DeHashed Search",
        description = "Search DeHashed breach intelligence for leaked credentials by email, username, domain, password or IP. Requires API key.",
        category = ToolCategory.DNS_WEB,
        parameters = listOf(
            ToolParam("query", ParamType.STRING, "Search value (email, username, domain, password or IP)"),
            ToolParam("queryType", ParamType.ENUM, "Type of query", required = false, defaultValue = "email",
                enumValues = listOf("email", "username", "domain", "password", "ip")),
            ToolParam("apiKey", ParamType.STRING, "DeHashed API key (email:apikey format supported)"),
        ),
        chainSuggestions = listOf("emailrep_check"),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val query = paramRequired(params, "query")
        val queryType = param(params, "queryType") ?: "email"
        val apiKey = paramRequired(params, "apiKey")

        // DeHashed basic auth: username = account email, password = API key.
        // The UI may pass the apiKey as "email:key"; split if so.
        val (user, pass) = if (apiKey.contains(":")) {
            apiKey.substringBefore(":") to apiKey.substringAfter(":")
        } else {
            "" to apiKey
        }
        if (user.isBlank()) {
            emit(ToolEvent.Error("DeHashed API key must be in 'email:apikey' format."))
            emit(ToolEvent.Done)
            return
        }

        val escapedQuery = query.replace(" ", "\\ ")
        val searchQuery = "$queryType:$escapedQuery"
        val url = "https://api.dehashed.com/search?query=${java.net.URLEncoder.encode(searchQuery, "UTF-8")}"

        emit(ToolEvent.Log("[DeHashed] Searching '$searchQuery'..."))

        withContext(Dispatchers.IO) {
            val request = Request.Builder()
                .url(url)
                .header("Authorization", Credentials.basic(user, pass))
                .header("Accept", "application/json")
                .header("User-Agent", "OptixV4")
                .build()

            runCatching {
                client.newCall(request).execute().use { response ->
                    val body = response.body?.string().orEmpty()
                    when (response.code) {
                        401, 403 -> {
                            emit(ToolEvent.Error("DeHashed authentication failed (${response.code}). Check your API key."))
                            return@use
                        }
                        429 -> {
                            emit(ToolEvent.Error("DeHashed rate limit reached (429). Try again later."))
                            return@use
                        }
                    }
                    if (body.isBlank()) {
                        emit(ToolEvent.Error("Empty response from DeHashed (HTTP ${response.code})."))
                        return@use
                    }
                    val json = JSONObject(body)
                    val total = json.optInt("total", 0)
                    val entries = json.optJSONArray("entries")

                    emit(ToolEvent.Result(OsintResult("Query", searchQuery, "DeHashed")))
                    emit(ToolEvent.Result(OsintResult("Total Matches", total.toString(), "DeHashed")))

                    val sampleCount = minOf(entries?.length() ?: 0, 10)
                    for (i in 0 until sampleCount) {
                        val entry = entries?.optJSONObject(i) ?: continue
                        val id = entry.optString("id", "?")
                        val name = entry.optString("name", "")
                        val dbName = entry.optString("database_name", "")
                        emit(ToolEvent.Result(OsintResult("Match #$i", "id=$id name=$name db=$dbName", "DeHashed")))
                    }

                    emit(ToolEvent.Log("[DeHashed] Found $total matching credential(s)/leak(s)."))
                }
            }.onFailure {
                emit(ToolEvent.Error("DeHashed request failed: ${it.message}"))
            }
        }

        emit(ToolEvent.ChainSuggestion("emailrep_check", mapOf("email" to query)))
        emit(ToolEvent.Done)
    }
}
