package chat.vojo.proxy.core

import android.util.Base64
import java.net.URI
import java.net.URLDecoder

/**
 * Parses a proxy share link into a [ProxyConfig]:
 *  - `ss://` SIP002 (`ss://base64url(method:password)@host:port#name`)
 *  - `ss://` legacy (`ss://base64(method:password@host:port)#name`)
 *  - `socks5://[user:pass@]host:port`
 *  - `http(s)://[user:pass@]host:port`
 * Returns null when the text is not a recognisable link.
 */
fun parseProxyUri(raw: String): ProxyConfig? {
    val s = raw.trim()
    return runCatching {
        when {
            s.startsWith("ss://", ignoreCase = true) -> parseSs(s)
            s.startsWith("socks5://", ignoreCase = true) || s.startsWith("socks://", ignoreCase = true) -> parseUri(s, ProxyType.SOCKS5, 1080)
            s.startsWith("https://", ignoreCase = true) -> parseUri(s, ProxyType.HTTPS, 443)
            s.startsWith("http://", ignoreCase = true) -> parseUri(s, ProxyType.HTTP, 8080)
            else -> null
        }
    }.getOrNull()
}

/**
 * True if [raw] is an `ss://` link that depends on an unsupported transport
 * plugin (obfs / v2ray-plugin). Lets the UI explain *why* a recognisable link
 * was rejected instead of falling back to the generic "no link" message.
 */
fun ssRequiresPlugin(raw: String): Boolean {
    val s = raw.trim()
    if (!s.startsWith("ss://", ignoreCase = true)) return false
    val query = s.substringBefore('#').substringAfter('?', "")
    return Regex("(^|&)plugin=[^&]+").containsMatchIn(query)
}

private fun decodeBase64(s: String): String? {
    val flagsList = listOf(
        Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP,
        Base64.DEFAULT,
    )
    for (flags in flagsList) {
        runCatching { return String(Base64.decode(s, flags), Charsets.UTF_8) }
    }
    return null
}

private fun splitHostPort(hostPort: String): Pair<String, Int>? {
    val hp = hostPort.trim()
    return if (hp.startsWith("[")) { // [v6]:port
        val host = hp.substringAfter('[').substringBefore(']')
        val port = hp.substringAfterLast(':').toIntOrNull() ?: return null
        host to port
    } else {
        val host = hp.substringBeforeLast(':')
        val port = hp.substringAfterLast(':').toIntOrNull() ?: return null
        host to port
    }
}

private fun parseSs(s: String): ProxyConfig? {
    val name = s.substringAfter('#', "").let { runCatching { URLDecoder.decode(it, "UTF-8") }.getOrDefault(it) }
    // SIP002 transport plugins (v2ray-plugin, obfs, …) aren't supported — a
    // config that needs one won't tunnel, so reject it rather than import a
    // silently-broken server.
    if (ssRequiresPlugin(s)) return null
    // The scheme is matched case-insensitively at dispatch, so strip it the same
    // way — a mixed-case `Ss://` must not survive into the body and break parsing.
    val body = s.replaceFirst(Regex("^ss://", RegexOption.IGNORE_CASE), "")
        .substringBefore('#').substringBefore('?').trimEnd('/')

    val method: String
    val password: String
    val host: String
    val port: Int
    if ('@' in body) {
        // SIP002: userinfo is base64url(method:password) or plain method:password
        val userinfo = body.substringBeforeLast('@')
        val decoded = decodeBase64(userinfo)?.takeIf { ':' in it } ?: userinfo
        if (':' !in decoded) return null
        method = decoded.substringBefore(':')
        password = decoded.substringAfter(':')
        val hp = splitHostPort(body.substringAfterLast('@')) ?: return null
        host = hp.first; port = hp.second
    } else {
        // Legacy: whole body is base64(method:password@host:port)
        val decoded = decodeBase64(body) ?: return null
        if ('@' !in decoded || ':' !in decoded) return null
        val cred = decoded.substringBeforeLast('@')
        method = cred.substringBefore(':')
        password = cred.substringAfter(':')
        val hp = splitHostPort(decoded.substringAfterLast('@')) ?: return null
        host = hp.first; port = hp.second
    }

    val cipher = SsCipher.entries.firstOrNull { it.id.equals(method, ignoreCase = true) } ?: return null
    if (host.isBlank() || port !in 1..65535) return null
    return ProxyConfig(
        name = name.ifBlank { host },
        type = ProxyType.SHADOWSOCKS,
        host = host,
        port = port,
        password = password,
        cipher = cipher,
    )
}

private fun parseUri(s: String, type: ProxyType, defaultPort: Int): ProxyConfig? {
    val uri = URI(s)
    val host = uri.host ?: return null
    val port = if (uri.port > 0) uri.port else defaultPort
    val user = uri.userInfo?.substringBefore(':').orEmpty()
    val pass = uri.userInfo?.substringAfter(':', "").orEmpty()
    val name = uri.fragment?.let { runCatching { URLDecoder.decode(it, "UTF-8") }.getOrDefault(it) }.orEmpty()
    return ProxyConfig(
        name = name.ifBlank { host },
        type = type,
        host = host,
        port = port,
        username = user,
        password = pass,
    )
}
