package com.optix.android.tools.network

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.parser.NmapParser
import com.optix.android.tools.registry.BaseTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext

/** Nmap — the industry-standard network scanner. Requires Termux root. */
class NmapTool : BaseTool() {
    override val metadata = ToolMetadata(
        name = "nmap_termux",
        displayName = "Nmap (Termux)",
        description = "Industry-standard network scanner: OS detection, service versions, NSE scripts",
        category = ToolCategory.NETWORK_SCANNING,
        requiresRoot = true,
        requiredPackages = listOf("nmap"),
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target IP, hostname, or CIDR range"),
            ToolParam("scan_type", ParamType.ENUM, "Scan type", required = false, defaultValue = "-sV",
                enumValues = listOf("-sV", "-sC", "-A", "-sS", "-sU", "-sn", "-O")),
            ToolParam("ports", ParamType.STRING, "Port range (e.g. 1-1000, 80,443)", required = false, defaultValue = "1-1000"),
            ToolParam("timing", ParamType.ENUM, "Timing template", required = false, defaultValue = "-T3",
                enumValues = listOf("-T1", "-T2", "-T3", "-T4", "-T5")),
            ToolParam("extra_flags", ParamType.STRING, "Extra flags (e.g. --script vuln)", required = false, defaultValue = ""),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val scanType = param(params, "scan_type") ?: "-sV"
        val ports = param(params, "ports") ?: "1-1000"
        val timing = param(params, "timing") ?: "-T3"
        val extra = param(params, "extra_flags") ?: ""

        emit(ToolEvent.Log("Starting Nmap $scanType scan on $target (ports: $ports)..."))
        emit(ToolEvent.Log("This may take a while depending on target and timing template."))

        val args = mutableListOf(scanType, "-p", ports, timing)
        if (extra.isNotBlank()) args.addAll(extra.split("\\s+".toRegex()))
        args.add(target)

        // Capture the raw output so it can be parsed into structured OpenPort /
        // ServiceInfo objects instead of only streaming raw log lines.
        val result = runCatching {
            shellExecutor?.execTermux("nmap", args, "nmap", emit)
        }.getOrNull()

        result?.stdout?.let { raw ->
            val report = NmapParser.parse(raw, fallbackHost = target)
            report.openPorts
                .filter { it.state.equals("open", true) }
                .forEach { emit(ToolEvent.Result(it)) }
            report.services
                .filter { !it.product.isNullOrBlank() }
                .forEach { emit(ToolEvent.Result(it)) }
            if (report.openPorts.any { it.state.equals("open", true) }) {
                emit(ToolEvent.Log("[Nmap] Parsed ${report.openPorts.count { it.state.equals("open", true) }} open port(s)."))
            }
        }
        emit(ToolEvent.Done)
    }
}

/** MasscanTool — ultra-fast asynchronous port scanner. */
class MasscanTool : BaseTool() {
    override val metadata = ToolMetadata(
        name = "masscan_termux",
        displayName = "Masscan (Termux)",
        description = "Ultra-fast port scanner — scans entire subnets in seconds",
        category = ToolCategory.NETWORK_SCANNING,
        requiresRoot = true,
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target IP or CIDR (e.g. 192.168.1.0/24)"),
            ToolParam("ports", ParamType.STRING, "Port range (e.g. 1-65535, 80,443)", required = false, defaultValue = "1-10000"),
            ToolParam("rate", ParamType.INT, "Packets per second (be careful on slow networks)", required = false, defaultValue = "1000"),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val ports = param(params, "ports") ?: "1-10000"
        val rate = paramInt(params, "rate", 1000)

        emit(ToolEvent.Log("Starting Masscan on $target (rate: $rate pps)..."))
        shellExecutor?.executeTermuxAsRoot("masscan", listOf(target, "-p", ports, "--rate", rate.toString()), "masscan", emit)
        emit(ToolEvent.ChainSuggestion("nmap_termux", mapOf("target" to target)))
        emit(ToolEvent.Done)
    }
}

/** ArpScanTool — native ARP-based LAN discovery. */
class ArpScanTool : BaseTool() {
    override val metadata = ToolMetadata(
        name = "arp_scan",
        displayName = "ARP Scan",
        description = "Fast LAN host discovery using ARP — more reliable than ping sweeps",
        category = ToolCategory.NETWORK_SCANNING,
        requiresRoot = true,
        parameters = listOf(
            ToolParam("interface", ParamType.STRING, "Network interface (e.g. wlan0, eth0)", required = false, defaultValue = "wlan0"),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val iface = param(params, "interface") ?: "wlan0"
        emit(ToolEvent.Log("ARP scanning on $iface — reading neighbor cache and probing..."))

        withContext(Dispatchers.IO) {
            // Try native ip neighbor first, fallback to arp-scan via Termux
            try {
                var found = 0
                val process = ProcessBuilder("su", "-c", "ip neigh show dev $iface").start()
                val reader = process.inputStream.bufferedReader()
                while (true) {
                    if (!kotlinx.coroutines.currentCoroutineContext().isActive) { process.destroy(); break }
                    val line = reader.readLine() ?: break
                    if (line.contains("REACHABLE") || line.contains("STALE") || line.contains("DELAY")) {
                        emit(ToolEvent.Log("  ✓ $line"))
                        found++
                    }
                }
                process.waitFor()
                emit(ToolEvent.Log("ARP cache scan complete — $found entries found."))
                emit(ToolEvent.Log("For deeper scan run: arp-scan --interface=$iface --localnet (requires Termux)"))
            } catch (e: Exception) {
                emit(ToolEvent.Error("ARP scan failed: ${e.message}"))
            }
        }
        emit(ToolEvent.ChainSuggestion("nmap_termux", mapOf("target" to iface)))
        emit(ToolEvent.Done)
    }
}

