package com.sshproxy.vpn.vpn

import android.content.Intent
import android.net.VpnService
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
import android.system.OsConstants
import com.sshproxy.vpn.TunnelApplication
import com.sshproxy.vpn.service.TunnelServiceHolder
import java.util.concurrent.atomic.AtomicBoolean

class ProxyVpnService : VpnService() {

    private var tunInterface: ParcelFileDescriptor? = null
    private val running = AtomicBoolean(false)
    private val statsHandler = Handler(Looper.getMainLooper())
    private val statsRunnable = object : Runnable {
        override fun run() {
            if (!running.get()) {
                return
            }
            val stats = Tun2Socks.getStats()
            if (stats != null) {
                sendBroadcast(
                    Intent(ACTION_TRAFFIC_STATS)
                        .setPackage(packageName)
                        .putExtra(EXTRA_RX_BYTES, stats.first)
                        .putExtra(EXTRA_TX_BYTES, stats.second)
                )
            }
            statsHandler.postDelayed(this, STATS_INTERVAL_MS)
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent?.action == ACTION_STOP) {
            stopTunnel()
            stopSelf()
            return START_NOT_STICKY
        }

        val socksPort = intent?.getIntExtra(EXTRA_SOCKS_PORT, 10808) ?: 10808
        val sshManager = TunnelServiceHolder.sshManager
        if (sshManager == null || !sshManager.isConnected) {
            stopSelf()
            return START_NOT_STICKY
        }

        if (running.get()) {
            return START_STICKY
        }

        sshManager.protectSshSocket(this)

        val builder = Builder()
            .setSession("SSH Proxy")
            .setMtu(MTU)
            .addAddress(TUN_ADDRESS, 30)
            .addRoute("0.0.0.0", 0)
            .addDnsServer("1.1.1.1")
            .addDnsServer("8.8.8.8")

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            builder.allowFamily(OsConstants.AF_INET)
        }

        applyDisallowedApplications(builder)

        tunInterface = builder.establish()
        if (tunInterface == null) {
            stopSelf()
            return START_NOT_STICKY
        }

        val tunFd = tunInterface!!.fd
        if (!Tun2Socks.start(
            context = this,
            tunFd = tunFd,
            mtu = MTU,
            socksHost = "127.0.0.1",
            socksPort = socksPort
        )) {
            val detail = Tun2Socks.loadErrorMessage()
            TunnelServiceHolder.onNativeError(
                buildString {
                    append("Native tun2socks недоступен.")
                    if (!detail.isNullOrBlank()) {
                        append(' ')
                        append(detail)
                    }
                    append(" Пересоберите проект: Build → Clean Project → Rebuild Project.")
                }
            )
            stopTunnel()
            stopSelf()
            return START_NOT_STICKY
        }

        running.set(true)
        sendBroadcast(
            Intent(com.sshproxy.vpn.service.TunnelService.ACTION_VPN_UP).setPackage(packageName)
        )
        statsHandler.removeCallbacks(statsRunnable)
        statsHandler.post(statsRunnable)
        return START_STICKY
    }

    override fun onDestroy() {
        stopTunnel()
        super.onDestroy()
    }

    private fun stopTunnel() {
        statsHandler.removeCallbacks(statsRunnable)
        Tun2Socks.stop()
        tunInterface?.close()
        tunInterface = null
        if (running.compareAndSet(true, false)) {
            sendBroadcast(
                Intent(ACTION_TRAFFIC_STATS)
                    .setPackage(packageName)
                    .putExtra(EXTRA_RX_BYTES, 0L)
                    .putExtra(EXTRA_TX_BYTES, 0L)
            )
        }
    }

    private fun applyDisallowedApplications(builder: Builder) {
        // Своё приложение всегда вне VPN — иначе SSH попадёт в туннель.
        runCatching { builder.addDisallowedApplication(packageName) }

        val excluded = (application as TunnelApplication).prefs.getExcludedPackages()
        excluded.forEach { pkg ->
            if (pkg != packageName) {
                runCatching { builder.addDisallowedApplication(pkg) }
            }
        }
    }

    companion object {
        const val ACTION_STOP = "com.sshproxy.vpn.STOP_VPN"
        const val ACTION_TRAFFIC_STATS = "com.sshproxy.vpn.TRAFFIC_STATS"
        const val EXTRA_SOCKS_PORT = "socks_port"
        const val EXTRA_RX_BYTES = "rx_bytes"
        const val EXTRA_TX_BYTES = "tx_bytes"

        private const val TUN_ADDRESS = "10.8.0.2"
        private const val MTU = 1500
        private const val STATS_INTERVAL_MS = 1000L
    }
}
