package com.optix.android.tools.web

import com.optix.android.domain.model.ParamType
import com.optix.android.domain.model.Severity
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.parser.NucleiParser
import com.optix.android.tools.registry.BaseTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File

/**
 * Nuclei vulnerability scanner — primarily a Termux/CLI tool.
 *
 * Runs `nuclei` via the injected [shellExecutor] against a target host and
 * optionally parses the JSON findings for CVEs and severity levels.
 *
 * The JSON report is written to a world-readable location on shared storage
 * (`/sdcard/Optix/nuclei_output.json`) so the app process can read and parse it
 * after the Termux process finishes.
 */
class NucleiTool : BaseTool() {

    override val metadata = ToolMetadata(
        name = "nuclei_termux",
        displayName = "Nuclei Scanner (Termux)",
        description = "Fast template-based vulnerability scanner for web apps, APIs, networks and CVEs. Requires Termux.",
        category = ToolCategory.EXPLOITATION,
        requiresRoot = false,
        requiredPackages = listOf("nuclei"),
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target URL or host (e.g. https://example.com)"),
            ToolParam("templatePath", ParamType.STRING, "Custom template/path (optional)", required = false, defaultValue = ""),
            ToolParam("severityFilter", ParamType.ENUM, "Severity filter", required = false, defaultValue = "all",
                enumValues = listOf("all", "critical", "high")),
            ToolParam("outputFormat", ParamType.ENUM, "Output format", required = false, defaultValue = "json",
                enumValues = listOf("json", "text")),
        ),
        chainSuggestions = listOf("http_inspector", "wpscan_termux", "nikto_termux"),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val templatePath = param(params, "templatePath") ?: ""
        val severityFilter = param(params, "severityFilter") ?: "all"
        val outputFormat = param(params, "outputFormat") ?: "json"

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

        val outputFile = File("/sdcard/Optix/nuclei_output.json")

        val args = mutableListOf("-u", target, "-silent")
        if (severityFilter != "all") args.addAll(listOf("-severity", severityFilter))
        when (outputFormat) {
            "json" -> {
                args.add("-json")
                args.addAll(listOf("-o", outputFile.absolutePath))
            }
            "text" -> args.add("-nc")
        }
        if (templatePath.isNotBlank()) args.addAll(listOf("-t", templatePath))

        emit(ToolEvent.Log("[Nuclei] Scanning $target (severity=$severityFilter, format=$outputFormat)..."))

        withContext(Dispatchers.IO) {
            shellExecutor?.executeTermuxAsRoot("nuclei", args, "nuclei", emit)
        }

        if (outputFormat == "json") {
            parseFindings(outputFile, emit)
        }

        emit(ToolEvent.ChainSuggestion("http_inspector", mapOf("url" to target)))
        emit(ToolEvent.Done)
    }

    private suspend fun parseFindings(outputFile: File, emit: suspend (ToolEvent) -> Unit) {
        if (!outputFile.exists()) {
            emit(ToolEvent.Log("[Nuclei] No JSON report produced (nuclei may be missing or returned nothing)."))
            return
        }
        runCatching {
            val text = outputFile.readText().trim()
            if (text.isEmpty()) {
                emit(ToolEvent.Log("[Nuclei] Scan complete — no findings."))
                return
            }
            // Delegate to the pure, unit-testable parser (item #12/#7).
            val findings = NucleiParser.parse(text)

            var critical = 0
            var high = 0
            findings.forEach { finding ->
                when (finding.severity) {
                    Severity.CRITICAL -> critical++
                    Severity.HIGH -> high++
                    else -> Unit
                }
                emit(ToolEvent.Result(finding))
                emit(ToolEvent.Log("  [${finding.severity}] ${finding.title}"))
            }

            emit(ToolEvent.Log(
                "[Nuclei] Done — ${findings.size} finding(s): $critical critical, $high high."
            ))
        }.onFailure {
            emit(ToolEvent.Error("Failed to parse Nuclei JSON output: ${it.message}"))
        }
    }
}
