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.parser.SshBannerParser
import com.optix.android.tools.registry.BaseTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.Socket

/** SSH banner grabber and algorithm auditor — pure JVM, no root needed. */
class SSHAuditTool : BaseTool() {
    override val metadata = ToolMetadata(
        name = "ssh_audit",
        displayName = "SSH Audit",
        description = "Grab SSH banner, server version, and check for weak configurations",
        category = ToolCategory.NETWORK_SCANNING,
        requiresNetwork = true,
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target IP or hostname"),
            ToolParam("port", ParamType.INT, "SSH port", required = false, defaultValue = "22"),
            ToolParam("timeout_ms", ParamType.INT, "Timeout (ms)", required = false, defaultValue = "5000"),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val port = paramInt(params, "port", 22)
        val timeout = paramInt(params, "timeout_ms", 5000)

        emit(ToolEvent.Log("Connecting to $target:$port for SSH audit..."))

        withContext(Dispatchers.IO) {
            try {
                val socket = Socket()
                socket.connect(java.net.InetSocketAddress(target, port), timeout)
                socket.soTimeout = timeout

                val reader = socket.getInputStream().bufferedReader()
                val writer = socket.getOutputStream().bufferedWriter()

                // Read SSH banner
                val banner = reader.readLine() ?: "No banner received"
                emit(ToolEvent.Log("SSH Banner: $banner"))
                emit(ToolEvent.Result(OsintResult("Banner", banner, "SSH")))

                // Analyze banner
                analyzeSSHBanner(banner, target, port, emit)

                // Send client identification
                writer.write("SSH-2.0-Optix_Security_Auditor\r\n")
                writer.flush()

                socket.close()
                emit(ToolEvent.Log("SSH audit complete."))
                emit(ToolEvent.ChainSuggestion("hydra_termux", mapOf("target" to target, "service" to "ssh")))
            } catch (e: java.net.ConnectException) {
                emit(ToolEvent.Error("Connection refused — SSH not running on $target:$port"))
            } catch (e: java.net.SocketTimeoutException) {
                emit(ToolEvent.Error("Timeout — $target:$port did not respond within ${timeout}ms"))
            } catch (e: Exception) {
                emit(ToolEvent.Error("SSH audit error: ${e.message}"))
            }
        }
        emit(ToolEvent.Done)
    }

    private suspend fun analyzeSSHBanner(
        banner: String,
        host: String,
        port: Int,
        emit: suspend (ToolEvent) -> Unit,
    ) {
        // Structured parse: emit ServiceInfo + typed VulnerabilityFinding objects
        // so the UI can show severity chips and filter, rather than raw log lines.
        val audit = SshBannerParser.parse(banner, host, port)
        emit(ToolEvent.Result(audit.service))
        audit.findings.forEach { finding ->
            emit(ToolEvent.Result(finding))
            emit(ToolEvent.Log("⚠️  [${finding.severity}] ${finding.title}: ${finding.detail}"))
        }
        when {
            banner.contains("SSH-1", ignoreCase = true) && !banner.contains("SSH-1.99", true) ->
                emit(ToolEvent.Log("⚠️  CRITICAL: SSHv1 detected — vulnerable to man-in-the-middle attacks!"))
            banner.contains("SSH-2.0") -> emit(ToolEvent.Log("✓  SSH 2.0 protocol (good)"))
        }
    }
}
