package com.optix.android.tools.parser

import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import com.optix.android.domain.model.Severity
import com.optix.android.domain.model.VulnerabilityFinding

/**
 * Pure parser for nuclei's JSON-Lines output (one JSON object per finding,
 * emitted by `nuclei -json`). Uses Gson (a plain JVM dependency) instead of
 * org.json so it can be unit-tested off-device.
 */
object NucleiParser {

    private val gson = Gson()

    /** Parse the full JSON-Lines report into structured findings. */
    fun parse(reportText: String, fallbackTarget: String = "unknown"): List<VulnerabilityFinding> {
        val text = reportText.trim()
        if (text.isEmpty()) return emptyList()
        return text.lineSequence()
            .map { it.trim() }
            .filter { it.isNotEmpty() }
            .mapNotNull { parseLine(it, fallbackTarget) }
            .toList()
    }

    /** Parse a single JSON-Lines record. Returns null if the line is not valid JSON. */
    fun parseLine(line: String, fallbackTarget: String = "unknown"): VulnerabilityFinding? {
        val obj = runCatching { gson.fromJson(line, JsonObject::class.java) }
            .getOrElse { if (it is JsonSyntaxException) return null else throw it }
            ?: return null

        val info = obj.getAsJsonObject("info")
        val severityStr = info?.get("severity")?.asStringOrNull()
        val name = info?.get("name")?.asStringOrNull()
            ?: obj.get("template-id")?.asStringOrNull()
            ?: "unknown"
        val description = info?.get("description")?.asStringOrNull().orEmpty()

        val target = obj.get("host")?.asStringOrNull()
            ?: obj.get("matched-at")?.asStringOrNull()
            ?: fallbackTarget

        val cve = extractCve(info)
        val severity = Severity.from(severityStr)

        return VulnerabilityFinding(
            tool = "nuclei",
            target = target,
            severity = severity,
            title = cve.ifBlank { name },
            detail = description.ifBlank { name },
            reference = cve.ifBlank { obj.get("template-id")?.asStringOrNull() },
            cvss = cvssForSeverity(severity),
        )
    }

    private fun extractCve(info: JsonObject?): String {
        if (info == null) return ""
        info.getAsJsonObject("classification")
            ?.getAsJsonArray("cve-id")
            ?.firstOrNull()
            ?.asStringOrNull()
            ?.let { if (it.isNotBlank()) return it.uppercase() }

        info.getAsJsonArray("tags")?.forEach { el ->
            val tag = el.asStringOrNull().orEmpty()
            if (tag.startsWith("cve-", ignoreCase = true)) return tag.uppercase()
        }
        return ""
    }

    private fun cvssForSeverity(severity: Severity): Double = when (severity) {
        Severity.CRITICAL -> 9.8
        Severity.HIGH -> 7.5
        Severity.MEDIUM -> 5.0
        Severity.LOW -> 2.5
        Severity.INFO -> 0.0
    }

    private fun com.google.gson.JsonElement?.asStringOrNull(): String? =
        runCatching { if (this == null || this.isJsonNull) null else this.asString }.getOrNull()
}
