package com.optix.android.tools.exploit

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.isActive
import kotlinx.coroutines.withContext

/**
 * Windows privilege escalation enumeration (winPEAS wrapper).
 *
 * Android cannot execute Windows binaries, so this tool drives winPEAS through
 * Termux — either by uploading winPEAS.exe via SMB (smbclient pattern) and running
 * it on the paired Windows host, or by invoking it over WinRM / SSH. Emits any
 * collected output as a structured [ToolEvent.Result].
 */
data class WinPeasResult(
    val target: String,
    val method: String,
    val command: String,
    val notes: List<String>,
)

class WinPEASTool : BaseTool() {

    /** Documented Termux requirement (ShellExecutor enforces availability at runtime). */
    private val requiresTermux: Boolean = true

    override val metadata = ToolMetadata(
        name = "winpeas_termux",
        displayName = "WinPEAS (Windows PrivEsc)",
        description = "Windows privilege escalation enumeration via winPEAS — SMB upload, WinRM (evil-winrm) or SSH execution",
        category = ToolCategory.LPE,
        requiresRoot = false,    // runs against a remote Windows host; Termux is required
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target Windows host (IP or hostname)"),
            ToolParam(
                "method", ParamType.ENUM, "Delivery / execution method",
                defaultValue = "smb",
                enumValues = listOf("smb", "winrm", "ssh"),
            ),
            ToolParam("username", ParamType.STRING, "Username for auth", required = false, defaultValue = ""),
            ToolParam("password", ParamType.STRING, "Password for auth", required = false, defaultValue = ""),
            ToolParam("share", ParamType.STRING, "SMB share name (smb method)", required = false, defaultValue = "C$"),
        ),
        chainSuggestions = listOf("cme_termux", "rubeus_termux"),
    )

    override fun validate(params: Map<String, Any>): List<String> {
        val errors = super.validate(params).toMutableList()
        val method = param(params, "method") ?: "smb"
        if (method !in listOf("smb", "winrm", "ssh")) errors.add("'method' must be 'smb', 'winrm' or 'ssh'")
        return errors
    }

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val method = param(params, "method") ?: "smb"
        val username = param(params, "username") ?: ""
        val password = param(params, "password") ?: ""
        val share = param(params, "share") ?: "C$"

        emit(ToolEvent.Log("Preparing winPEAS enumeration against $target via $method (Termux required)."))

        val command = buildCommand(method, target, username, password, share)
        val notes = buildNotes(method, target)

        notes.forEach { emit(ToolEvent.Log(it)) }
        emit(ToolEvent.Log("▶ Constructed command:"))
        emit(ToolEvent.Log("  $command"))

        // Attempt to run through Termux (binary presence handled by ShellExecutor).
        withContext(Dispatchers.IO) {
            try {
                val (tool, args) = splitCommand(command)
                shellExecutor?.executeTermuxAsRoot(tool, args, tool, emit)
            } catch (e: Exception) {
                emit(ToolEvent.Error("winPEAS execution failed: ${e.message}"))
            }
        }

        emit(ToolEvent.Result(WinPeasResult(target = target, method = method, command = command, notes = notes)))
        emit(ToolEvent.ChainSuggestion("cme_termux", mapOf("target" to target, "protocol" to "smb")))
        emit(ToolEvent.Done)
    }

    private fun buildCommand(method: String, target: String, user: String, pass: String, share: String): String {
        val auth = if (user.isBlank()) "" else "-U '$user%$pass'"
        return when (method) {
            "smb" -> {
                // Upload winPEAS.exe to an SMB share, then it must be run on the host.
                // smbclient upload pattern (mirrors SmbClientTool):
                //   smbclient //HOST/SHARE -U user%pass -c 'put winPEASx64.exe'
                "smbclient //$target/$share $auth -c 'put winPEASx64.exe winPEASx64.exe' && " +
                    "echo 'Then execute on the host: \\\\$target\\$share\\winPEASx64.exe > peas.txt'"
            }
            "winrm" -> {
                // evil-winrm session; run winPEAS once uploaded or already present.
                "evil-winrm -i $target -u '$user' -p '$pass' -x 'winPEASx64.exe quiet'"
            }
            else -> { // ssh
                "ssh ${if (user.isBlank()) "" else "$user@"}$target 'winPEASx64.exe quiet'"
            }
        }
    }

    private fun buildNotes(method: String, target: String): List<String> = buildList {
        add("Android cannot run Windows binaries natively; winPEAS must execute on the Windows host.")
        when (method) {
            "smb" -> add("Place winPEASx64.exe in Termux home first: cp winPEASx64.exe ~/ ; ensure the SMB share is writable.")
            "winrm" -> add("Install via Termux: gem install evil-winrm (or pkg install evil-winrm).")
            "ssh" -> add("Use passwordless SSH key auth for non-interactive execution: ssh-copy-id user@$target.")
        }
        add("Parse the resulting peas.txt for 'Interesting' / 'Privileged' sections to identify escalation paths.")
    }

    private fun splitCommand(command: String): Pair<String, List<String>> {
        val parts = command.split(" ").filter { it.isNotBlank() }
        // The command may contain chained sub-shells; run the leading binary for Termux and
        // log the full pipeline so the user can copy it verbatim.
        val head = parts.firstOrNull() ?: "sh"
        return head to listOf("-c", command)
    }
}
