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.Dispatchers
import kotlinx.coroutines.withContext
import java.net.InetAddress

class TracerouteTool : BaseTool() {

    override val metadata = ToolMetadata(
        name = "traceroute",
        displayName = "Traceroute",
        description = "Trace the network path to a target host (ICMP TTL probes)",
        category = ToolCategory.NETWORK_SCANNING,
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target hostname or IP"),
            ToolParam("max_hops", ParamType.INT, "Maximum hops", required = false, defaultValue = "30"),
            ToolParam("timeout_ms", ParamType.INT, "Timeout per hop (ms)", required = false, defaultValue = "1000"),
        ),
    )

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

        emit(ToolEvent.Log("Traceroute to $target (max $maxHops hops)"))

        withContext(Dispatchers.IO) {
            for (ttl in 1..maxHops) {
                val start = System.currentTimeMillis()
                val process = runCatching {
                    ProcessBuilder("ping", "-c", "1", "-t", ttl.toString(), "-W", (timeoutMs / 1000).coerceAtLeast(1).toString(), target)
                        .redirectErrorStream(true)
                        .start()
                }.getOrNull()

                val output = process?.inputStream?.bufferedReader()?.readText() ?: ""
                process?.waitFor()
                val elapsed = System.currentTimeMillis() - start

                val fromMatch = Regex("""From\s+([0-9a-zA-Z.-]+)""").find(output)
                val bytesMatch = Regex("""bytes from\s+([0-9a-zA-Z.-]+)""").find(output)

                var hopIp = "*"
                var reachedTarget = false

                if (bytesMatch != null) {
                    hopIp = bytesMatch.groupValues[1]
                    reachedTarget = true
                } else if (fromMatch != null) {
                    hopIp = fromMatch.groupValues[1]
                }

                val hopName = if (hopIp != "*") {
                    runCatching { InetAddress.getByName(hopIp).canonicalHostName }.getOrDefault(hopIp)
                } else ""

                val displayHopName = if (hopName != hopIp && hopName.isNotEmpty()) hopName else ""

                emit(ToolEvent.Result(OsintResult(
                    field = "Hop $ttl",
                    value = "$hopIp  $displayHopName  ${elapsed}ms",
                    source = "traceroute",
                )))
                emit(ToolEvent.Log("  $ttl  $hopIp  $displayHopName  ${elapsed}ms"))

                if (reachedTarget) break
            }
        }

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