package com.optix.android.tools.network

import com.optix.android.domain.model.OsintResult
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.isActive
import kotlinx.coroutines.withContext
import kotlinx.coroutines.Dispatchers
import okhttp3.OkHttpClient
import okhttp3.Request

/** Network Topology Mapper — combines traceroute + ASN + rDNS. */
class NetworkTopologyTool(private val client: OkHttpClient) : BaseTool() {
    override val metadata = ToolMetadata(
        name = "network_topology",
        displayName = "Network Topology",
        description = "Map the full network path to a target: traceroute + ASN lookup + reverse DNS for each hop. Reveals ISPs, CDNs, and routing infrastructure.",
        category = ToolCategory.NETWORK_SCANNING,
        requiresNetwork = true,
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target hostname or IP"),
            ToolParam("max_hops", ParamType.INT, "Maximum hops to trace", required = false, defaultValue = "20"),
        ),
        chainSuggestions = listOf("traceroute", "port_scanner", "nmap"),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val maxHops = paramInt(params, "max_hops", 20)

        emit(ToolEvent.Log("Mapping network topology to $target (max $maxHops hops)..."))
        emit(ToolEvent.Log(""))

        withContext(Dispatchers.IO) {
            for (ttl in 1..maxHops) {
                try {
                    val start = System.currentTimeMillis()
                    val process = ProcessBuilder("ping", "-c", "1", "-W", "2", "-t", ttl.toString(), target).start()
                    val out = process.inputStream.bufferedReader().readText()
                    val err = process.errorStream.bufferedReader().readText()
                    process.waitFor()
                    val elapsed = System.currentTimeMillis() - start

                    val hopIpRegex = Regex("""From ([\d.]+)""")
                    val destRegex = Regex("""from ([\d.]+)""")
                    val hopIp = hopIpRegex.find(out + err)?.groupValues?.get(1)
                        ?: destRegex.find(out)?.groupValues?.get(1)

                    if (hopIp != null) {
                        val hostname = try {
                            java.net.InetAddress.getByName(hopIp).canonicalHostName
                        } catch (e: Exception) { hopIp }

                        var asn = "Unknown"
                        try {
                            val asnReq = Request.Builder()
                                .url("https://api.hackertarget.com/aslookup/?q=$hopIp")
                                .header("User-Agent", "Optix/3.0").build()
                            val asnResp = client.newCall(asnReq).execute()
                            val asnBody = asnResp.body?.string() ?: ""
                            asnResp.close()
                            if (asnBody.isNotBlank() && !asnBody.startsWith("error")) {
                                asn = asnBody.lines().firstOrNull()?.take(60) ?: "Unknown"
                            }
                        } catch (_: Exception) {}

                        val hopDisplay = "${"%-3s".format(ttl)}  ${"%-16s".format(hopIp)}  [${elapsed}ms]  $hostname"
                        emit(ToolEvent.Log(hopDisplay))
                        if (asn != "Unknown") emit(ToolEvent.Log("     ASN: $asn"))
                        emit(ToolEvent.Result(OsintResult("Hop $ttl", "$hopIp ($hostname) — $asn", "${elapsed}ms")))

                        if (hostname.contains(target) || hopIp == target ||
                            out.contains("1 packets transmitted, 1 received")) {
                            emit(ToolEvent.Log(""))
                            emit(ToolEvent.Log("✓ Reached destination in $ttl hops."))
                            break
                        }
                    } else {
                        emit(ToolEvent.Log("${"%-3s".format(ttl)}  * * *  (no response)"))
                    }

                    if (!kotlinx.coroutines.currentCoroutineContext().isActive) break
                } catch (e: Exception) {
                    emit(ToolEvent.Log("$ttl  error: ${e.message}"))
                }
            }
        }
        emit(ToolEvent.ChainSuggestion("port_scanner", mapOf("target" to target)))
        emit(ToolEvent.Done)
    }
}
