package com.optix.android.tools.network

import com.optix.android.domain.model.HostResult
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.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import java.io.File
import java.net.InetAddress

class LanScannerTool : BaseTool() {

    override val metadata = ToolMetadata(
        name = "lan_scanner",
        displayName = "LAN Scanner",
        description = "Discover all live hosts on the local network subnet",
        category = ToolCategory.NETWORK_SCANNING,
        parameters = listOf(
            ToolParam("subnet", ParamType.STRING, "Subnet to scan (e.g. 192.168.1)", required = false, defaultValue = "192.168.1"),
            ToolParam("timeout_ms", ParamType.INT, "Reachability timeout (ms)", required = false, defaultValue = "300"),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        var subnet = param(params, "subnet") ?: "192.168.1"
        
        // Strip out CIDR notation and ending .0 so "192.168.1.0/24" becomes "192.168.1"
        if (subnet.contains("/")) {
            subnet = subnet.substringBefore("/")
        }
        if (subnet.endsWith(".0")) {
            subnet = subnet.substringBeforeLast(".0")
        }

        val timeoutMs = paramInt(params, "timeout_ms", 300)

        emit(ToolEvent.Log("Scanning LAN: $subnet.1-254"))

        val semaphore = Semaphore(50)
        coroutineScope {
            (1..254).map { i ->
                async(Dispatchers.IO) {
                    semaphore.withPermit {
                        val ip = "$subnet.$i"
                        val reachable = pingCmd(ip, timeoutMs)
                        if (reachable) {
                            val hostname = runCatching { InetAddress.getByName(ip).canonicalHostName }.getOrNull()
                            val macAddress = resolveMacAddress(ip)
                            emit(
                                ToolEvent.Result(
                                    HostResult(
                                        ip = ip,
                                        hostname = hostname,
                                        status = "UP",
                                        macAddress = macAddress,
                                        deviceType = inferDeviceType(hostname, macAddress),
                                    )
                                )
                            )
                            emit(ToolEvent.Log("  $ip  UP  ${hostname ?: ""}"))
                        }
                        emit(ToolEvent.Progress(i, 254))
                    }
                }
            }.awaitAll()
        }

        // Suggest a deep port scan against the discovered subnet's first host, or just the subnet.
        emit(ToolEvent.ChainSuggestion("port_scanner", mapOf("target" to subnet)))
        emit(ToolEvent.Done)
    }

    private fun resolveMacAddress(ip: String): String? {
        return runCatching {
            File("/proc/net/arp")
                .takeIf { it.exists() }
                ?.readLines()
                ?.drop(1)
                ?.firstOrNull { line ->
                    val cols = line.trim().split(Regex("\\s+"))
                    cols.firstOrNull() == ip
                }
                ?.split(Regex("\\s+"))
                ?.getOrNull(3)
                ?.takeUnless { it == "00:00:00:00:00:00" }
        }.getOrNull()
    }

    private fun inferDeviceType(hostname: String?, macAddress: String?): String {
        val source = listOfNotNull(hostname, macAddress).joinToString(" ").lowercase()
        return when {
            "android" in source || "pixel" in source || "galaxy" in source || "phone" in source -> "Mobile"
            "iphone" in source || "ipad" in source || "ios" in source -> "Apple Mobile"
            "printer" in source || "hp" in source || "epson" in source || "brother" in source -> "Printer"
            "camera" in source || "cam" in source || "nest" in source || "ring" in source -> "Camera / IoT"
            "router" in source || "gateway" in source || "ubiquiti" in source || "tp-link" in source -> "Gateway / Router"
            "tv" in source || "roku" in source || "chromecast" in source || "firetv" in source -> "Media Device"
            "pc" in source || "desktop" in source || "laptop" in source || "workstation" in source -> "Computer"
            else -> "Network Host"
        }
    }
}
