package com.optix.android.tools.utility

import com.google.gson.JsonParser
import com.optix.android.domain.model.HashResult
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.OkHttpClient
import okhttp3.Request
import java.security.MessageDigest

class HashCrackerTool(private val client: OkHttpClient) : BaseTool() {

    override val metadata = ToolMetadata(
        name = "hash_cracker",
        displayName = "Hash Cracker",
        description = "Crack MD5/SHA1/SHA256 hashes via online lookup and local wordlist",
        category = ToolCategory.PAYLOADS_UTILITIES,
        parameters = listOf(
            ToolParam("hash", ParamType.STRING, "Hash to crack"),
            ToolParam("wordlist", ParamType.STRING, "Local wordlist (comma-separated)", required = false,
                defaultValue = "password,123456,admin,letmein,qwerty,root,toor,pass,test"),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val hash = paramRequired(params, "hash").lowercase().trim()
        val wordlist = (param(params, "wordlist") ?: "password,123456").split(",").map { it.trim() }
        val algorithm = detectAlgorithm(hash)

        emit(ToolEvent.Log("Cracking $algorithm hash: $hash"))

        // 1. Online lookup via md5decrypt.net (MD5 only)
        if (algorithm == "MD5") {
            withContext(Dispatchers.IO) {
                runCatching {
                    val req = Request.Builder()
                        .url("https://md5decrypt.net/Api/api.php?hash=$hash&hash_type=md5&email=&code=")
                        .build()
                    client.newCall(req).execute().use { r ->
                        val result = r.body?.string()?.trim() ?: ""
                        if (result.isNotEmpty() && result != "Not found") {
                            emit(ToolEvent.Result(HashResult(hash, algorithm, result)))
                            emit(ToolEvent.Log("  [ONLINE] $hash = $result"))
                            return@withContext
                        }
                    }
                }
            }
        }

        // 2. Local wordlist attack
        emit(ToolEvent.Log("  Trying local wordlist (${wordlist.size} words)..."))
        val digest = MessageDigest.getInstance(algorithm.replace("-", ""))
        wordlist.forEachIndexed { idx, word ->
            val candidate = digest.digest(word.toByteArray()).joinToString("") { "%02x".format(it) }
            if (candidate == hash) {
                emit(ToolEvent.Result(HashResult(hash, algorithm, word)))
                emit(ToolEvent.Log("  [FOUND] $hash = $word"))
                emit(ToolEvent.Done); return
            }
            emit(ToolEvent.Progress(idx + 1, wordlist.size))
        }

        emit(ToolEvent.Log("  Hash not cracked."))
        emit(ToolEvent.ChainSuggestion("dehashed_search", mapOf("query" to hash, "queryType" to "password")))
        emit(ToolEvent.Done)
    }

    private fun detectAlgorithm(hash: String): String = when (hash.length) {
        32   -> "MD5"
        40   -> "SHA-1"
        64   -> "SHA-256"
        128  -> "SHA-512"
        else -> "MD5"
    }
}
