package com.optix.android.tools.ai

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

/**
 * AI-assisted fuzzer — a smart wordlist/dictionary generator and fuzzing wrapper.
 *
 * Lightweight by design: it probes the target (mirroring [com.optix.android.tools.web.HttpInspectorTool])
 * to detect the technology stack, then selects an appropriate built-in wordlist/template and drives
 * `ffuf` (and optionally `nuclei`) via Termux. If an AI provider were available it could suggest
 * additional context-specific payloads; here we fall back to curated built-in templates.
 */
class AIAssistedFuzzerTool(
    private val client: OkHttpClient,
) : BaseTool() {

    override val metadata = ToolMetadata(
        name = "ai_fuzzer_termux",
        displayName = "AI-Assisted Fuzzer (Termux)",
        description = "Smart fuzzer that detects the target tech stack and drives ffuf/nuclei with context-aware wordlists. Requires Termux.",
        category = ToolCategory.PAYLOADS_UTILITIES,
        requiresRoot = false,
        parameters = listOf(
            ToolParam("targetUrl", ParamType.STRING, "Target base URL (e.g. https://example.com)"),
            ToolParam("context", ParamType.STRING, "Extra context/hint for payload selection", required = false, defaultValue = ""),
            ToolParam("mode", ParamType.ENUM, "Fuzzing mode", required = false, defaultValue = "directory",
                enumValues = listOf("directory", "parameter", "payload")),
        ),
        chainSuggestions = listOf("http_inspector", "nuclei_termux", "gobuster_termux"),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val targetUrl = paramRequired(params, "targetUrl").trimEnd('/')
        val context = param(params, "context") ?: ""
        val mode = param(params, "mode") ?: "directory"

        if (shellExecutor == null) {
            emit(ToolEvent.Error("ShellExecutor is unavailable — Termux execution is not possible."))
            emit(ToolEvent.Done)
            return
        }

        emit(ToolEvent.Log("[AI-Fuzzer] Profiling $targetUrl to select templates..."))

        val tech = withContext(Dispatchers.IO) { detectTechStack(targetUrl, emit) }
        emit(ToolEvent.Log("[AI-Fuzzer] Detected stack: ${tech.joinToString(", ")}"))

        val wordlist = selectWordlist(tech, context, mode)
        emit(ToolEvent.Log("[AI-Fuzzer] Selected wordlist: $wordlist (mode=$mode)"))

        val args = buildFfufArgs(targetUrl, wordlist, mode)
        withContext(Dispatchers.IO) {
            shellExecutor?.executeTermuxAsRoot("ffuf", args, "ffuf", emit)
        }

        emit(ToolEvent.ChainSuggestion("nuclei_termux", mapOf("target" to targetUrl)))
        emit(ToolEvent.Done)
    }

    private suspend fun detectTechStack(targetUrl: String, emit: suspend (ToolEvent) -> Unit): List<String> {
        val stack = mutableListOf<String>()
        runCatching {
            val request = Request.Builder().url(targetUrl).head().build()
            client.newCall(request).execute().use { response ->
                val server = response.header("Server").orEmpty()
                val poweredBy = response.header("X-Powered-By").orEmpty()
                val contentType = response.header("Content-Type").orEmpty()
                if (server.contains("nginx", true)) stack += "nginx"
                if (server.contains("apache", true)) stack += "apache"
                if (server.contains("IIS", true) || server.contains("microsoft", true)) stack += "iis"
                if (poweredBy.contains("PHP", true) || contentType.contains("php", true)) stack += "php"
                if (poweredBy.contains("ASP", true) || contentType.contains("asp", true)) stack += "aspnet"
                if (poweredBy.contains("Express", true) || server.contains("cloudflare", true)) stack += "node"
                if (contentType.contains("json", true)) stack += "api"
                emit(ToolEvent.Log("  Server: $server | X-Powered-By: $poweredBy"))
            }
        }.onFailure {
            emit(ToolEvent.Log("[AI-Fuzzer] Could not profile target (${it.message}); using generic templates."))
        }
        if (stack.isEmpty()) stack += "generic"
        return stack
    }

    private fun selectWordlist(tech: List<String>, context: String, mode: String): String {
        val base = when (mode) {
            "parameter" -> "/data/data/com.termux/files/usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt"
            "payload" -> "/data/data/com.termux/files/usr/share/wordlists/seclists/Fuzzings/SQLi/SQLi.txt"
            else -> "/data/data/com.termux/files/usr/share/wordlists/seclists/Discovery/Web-Content/common.txt"
        }
        // Context / tech-driven selection could swap in more specific lists.
        return if (context.isNotBlank() && tech.contains("api")) {
            "/data/data/com.termux/files/usr/share/wordlists/seclists/Discovery/Web-Content/api/actions.txt"
        } else {
            base
        }
    }

    private fun buildFfufArgs(targetUrl: String, wordlist: String, mode: String): List<String> {
        return when (mode) {
            "parameter" -> listOf("-u", "$targetUrl?FUZZ=test", "-w", wordlist, "-mc", "200-499", "-t", "40")
            "payload" -> listOf("-u", targetUrl, "-X", "POST", "-w", wordlist, "-mc", "200-499", "-t", "20")
            else -> listOf("-u", "$targetUrl/FUZZ", "-w", wordlist, "-mc", "200-399", "-t", "40", "-fc", "404")
        }
    }
}
