package com.pangolin.pangolin_vpn

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.TrafficStats
import android.net.VpnService
import android.os.Build
import android.os.ParcelFileDescriptor
import android.util.Log
import go.libbox.BoxService
import go.libbox.CommandClient
import go.libbox.CommandClientHandler
import go.libbox.CommandClientOptions
import go.libbox.Libbox
import go.libbox.OutboundGroupIterator
import go.libbox.PlatformInterface
import go.libbox.ProcessInfo
import go.libbox.StatusMessage
import go.libbox.StringIterator
import go.libbox.TunOptions
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean

/**
 * PangolinVpnService — VPN 前台服务，封装 sing-box libbox 隧道。
 *
 * ## 生命周期
 *   START → handleStart() → 后台线程 → startLibbox() → [BoxService.start()]
 *         → VpnEventBus.postStatus("on")
 *   STOP  → doStop() → [BoxService.close()] → 关 TUN fd → postStatus("off")
 *   REVOKE → onRevoke() → postStatus("error") → doStop()
 *
 * ## 通信
 *   Dart 通过 [MainActivity] MethodChannel 发送 start/stop intent。
 *   状态与统计经 [VpnEventBus] 推回 Flutter EventChannel。
 *
 * ## 注意：libbox.aar API
 *   依赖 app/kernel/dist/android/libbox.aar（由 build-android.sh 产出）。
 *   方法签名注释标注 "// libbox API" 处若编译失败，请对照实际 libbox.aar 中
 *   go.libbox.* 的 javadoc 微调参数名；整体架构不变。
 */
class PangolinVpnService : VpnService() {

    companion object {
        private const val TAG = "PangolinVpnService"

        const val ACTION_START = "com.pangolin.vpn.START"
        const val ACTION_STOP  = "com.pangolin.vpn.STOP"
        const val EXTRA_CONFIG = "config_json"

        private const val NOTIFICATION_CHANNEL_ID = "pangolin_vpn_channel"
        private const val NOTIFICATION_ID = 1001

        fun startVpn(context: Context, configJson: String) {
            val intent = Intent(context, PangolinVpnService::class.java).apply {
                action = ACTION_START
                putExtra(EXTRA_CONFIG, configJson)
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context.startForegroundService(intent)
            } else {
                context.startService(intent)
            }
            Log.d(TAG, "startVpn: intent dispatched, configLen=${configJson.length}")
        }

        fun stopVpn(context: Context) {
            val intent = Intent(context, PangolinVpnService::class.java).apply {
                action = ACTION_STOP
            }
            context.startService(intent)
            Log.d(TAG, "stopVpn: intent dispatched")
        }
    }

    // ── 内核与 TUN 状态 ───────────────────────────────────────────

    /** libbox BoxService 内核实例（主线程以外创建，通过 volatile 保证可见性） */
    @Volatile private var boxService: BoxService? = null

    /** TUN 文件描述符持有者（VpnService.Builder.establish() 返回值） */
    @Volatile var currentTunPfd: ParcelFileDescriptor? = null

    /** libbox CommandClient（统计与状态轮询） */
    @Volatile private var commandClient: CommandClient? = null

    /** 兜底统计定时器（CommandClient 不可用时启用 TrafficStats 方案） */
    private var statsExecutor: ScheduledExecutorService? = null

    /** 防止 doStop 重入 */
    private val stopping = AtomicBoolean(false)

    // ── 生命周期 ────────────────────────────────────────────────

    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "onCreate")
        createNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.i(TAG, "onStartCommand: action=${intent?.action}")
        return when (intent?.action) {
            ACTION_START -> {
                val configJson = intent.getStringExtra(EXTRA_CONFIG) ?: "{}"
                handleStart(configJson)
                START_STICKY
            }
            ACTION_STOP -> {
                doStop()
                START_NOT_STICKY
            }
            else -> {
                Log.w(TAG, "onStartCommand: unknown action, falling through")
                START_NOT_STICKY
            }
        }
    }

    override fun onRevoke() {
        // 系统撤销 VPN 权限（如用户在设置里关闭）→ 推 error 再清理
        Log.w(TAG, "onRevoke: permission revoked by system")
        VpnEventBus.postStatus("error")
        doStop()
    }

    override fun onDestroy() {
        Log.i(TAG, "onDestroy")
        doStop()
        super.onDestroy()
    }

    // ── 启动流程 ─────────────────────────────────────────────────

    private fun handleStart(configJson: String) {
        stopping.set(false)
        startForeground(NOTIFICATION_ID, buildNotification("正在连接…"))
        VpnEventBus.postStatus("connecting")

        // libbox 内核启动必须在后台线程（不能阻塞 onStartCommand）
        Thread {
            try {
                startLibbox(configJson)
            } catch (e: Exception) {
                Log.e(TAG, "startLibbox failed: $e", e)
                VpnEventBus.postStatus("error")
                updateNotification("连接失败")
                doStop()
            }
        }.apply { name = "pangolin-libbox-start" }.start()
    }

    /**
     * 在后台线程内：
     *   1. 构造 PlatformInterface（inner class，持有 VpnService 引用）
     *   2. 新建 BoxService 并 start()（内核会回调 openTun 建 TUN 接口）
     *   3. 启动 CommandClient 轮询统计
     *   4. 推送 "on" 状态
     */
    private fun startLibbox(configJson: String) {
        val platform = PangolinPlatformInterface()

        // ── libbox API: Libbox.newBoxService(platformInterface, configJson, needBuildConfig)
        //    如果编译报错，可能 API 为: new BoxService(platform, configJson)
        //    或: BoxService.newService(platform, configJson)
        val service = Libbox.newBoxService(platform, configJson, false) // libbox API
        service.start()                                                  // libbox API
        boxService = service
        Log.i(TAG, "BoxService started")

        // 启动统计客户端
        startCommandClientOrFallback()

        // 更新通知与状态
        updateNotification("加速已开启")
        VpnEventBus.postStatus("on")
    }

    // ── 统计：CommandClient + TrafficStats 兜底 ───────────────────

    /**
     * 优先使用 libbox CommandClient 获取精确统计；若连接失败则退回 Android TrafficStats。
     */
    private fun startCommandClientOrFallback() {
        try {
            startCommandClient()
        } catch (e: Exception) {
            Log.w(TAG, "CommandClient unavailable ($e), falling back to TrafficStats")
            startTrafficStatsFallback()
        }
    }

    private fun startCommandClient() {
        val options = CommandClientOptions()
        // Command 0 = STATUS（含流量统计）；间隔 1 秒（纳秒）
        // libbox API: options.Command / options.StatusInterval 为 gomobile 暴露的字段
        options.command = 0                        // libbox API field
        options.statusInterval = 1_000_000_000L   // libbox API field: nanoseconds

        val handler = object : CommandClientHandler {
            override fun connected() {
                Log.d(TAG, "CommandClient connected")
            }
            override fun disconnected(message: String) {
                Log.d(TAG, "CommandClient disconnected: $message")
                // 断开后不改变 VPN 状态——隧道仍在运行，仅统计中断
            }
            override fun writeLog(message: String) {
                Log.v("libbox", message)
            }
            override fun writeStatus(status: StatusMessage) {
                // libbox API: StatusMessage 提供累计流量与瞬时速率
                val stats = mapOf<String, Any>(
                    "uploadBytes"    to (status.uploadTotal()),   // libbox API
                    "downloadBytes"  to (status.downloadTotal()), // libbox API
                    "uploadSpeed"    to (status.uploadSpeed().toDouble()),   // libbox API
                    "downloadSpeed"  to (status.downloadSpeed().toDouble()), // libbox API
                    "urltestResults" to emptyList<Any>()
                )
                VpnEventBus.postStats(stats)
            }
            override fun writeGroups(groups: OutboundGroupIterator) { /* 暂不处理 urltest 延迟 */ }
            override fun initializeClashMode(modeList: StringIterator, currentMode: String) {}
            override fun updateClashMode(newMode: String) {}
        }

        // libbox API: Libbox.newCommandClient(handler, options)
        val client = Libbox.newCommandClient(handler, options) // libbox API
        client.connect()                                        // libbox API
        commandClient = client
        Log.i(TAG, "CommandClient started")
    }

    /**
     * 兜底：用 Android TrafficStats API 按 UID 统计网络流量。
     * 由于所有流量都走 VPN，UID 统计近似等于隧道流量。
     */
    private fun startTrafficStatsFallback() {
        val uid = android.os.Process.myUid()
        var lastRx = TrafficStats.getUidRxBytes(uid)
        var lastTx = TrafficStats.getUidTxBytes(uid)
        var totalRx = 0L
        var totalTx = 0L

        statsExecutor = Executors.newSingleThreadScheduledExecutor().also { exec ->
            exec.scheduleAtFixedRate({
                if (stopping.get()) return@scheduleAtFixedRate
                try {
                    val rx = TrafficStats.getUidRxBytes(uid).let { if (it < 0) 0L else it }
                    val tx = TrafficStats.getUidTxBytes(uid).let { if (it < 0) 0L else it }
                    val rxDiff = (rx - lastRx).coerceAtLeast(0)
                    val txDiff = (tx - lastTx).coerceAtLeast(0)
                    totalRx += rxDiff
                    totalTx += txDiff
                    lastRx = rx
                    lastTx = tx

                    val stats = mapOf<String, Any>(
                        "uploadBytes"    to totalTx,
                        "downloadBytes"  to totalRx,
                        "uploadSpeed"    to txDiff.toDouble(),
                        "downloadSpeed"  to rxDiff.toDouble(),
                        "urltestResults" to emptyList<Any>()
                    )
                    VpnEventBus.postStats(stats)
                } catch (e: Exception) {
                    Log.w(TAG, "TrafficStats error: $e")
                }
            }, 1L, 1L, TimeUnit.SECONDS)
        }
        Log.i(TAG, "TrafficStats fallback started, uid=$uid")
    }

    // ── 停止流程 ─────────────────────────────────────────────────

    /**
     * 幂等停止：关闭统计 → 关闭 BoxService → 关闭 TUN fd → 停前台通知。
     * 由多个入口（ACTION_STOP / onRevoke / onDestroy）调用，通过 [stopping] 防止重入。
     */
    private fun doStop() {
        if (!stopping.compareAndSet(false, true)) {
            Log.d(TAG, "doStop: already stopping, skip")
            return
        }
        Log.i(TAG, "doStop: shutting down tunnel")

        // 1. 停统计（先于 BoxService，避免最后一帧读到已关闭的 fd）
        statsExecutor?.apply {
            shutdownNow()
            try { awaitTermination(500, TimeUnit.MILLISECONDS) } catch (_: InterruptedException) {}
        }
        statsExecutor = null

        try {
            commandClient?.disconnect() // libbox API
        } catch (e: Exception) {
            Log.w(TAG, "commandClient.disconnect failed: $e")
        }
        commandClient = null

        // 2. 关闭 BoxService 内核（会在内部关闭它持有的 TUN fd 引用）
        try {
            boxService?.close() // libbox API
        } catch (e: Exception) {
            Log.w(TAG, "boxService.close failed: $e")
        }
        boxService = null

        // 3. 关闭 ParcelFileDescriptor（持有底层 fd；必须在 BoxService.close 之后）
        try {
            currentTunPfd?.close()
        } catch (e: Exception) {
            Log.w(TAG, "tunPfd.close failed: $e")
        }
        currentTunPfd = null

        // 4. 停前台 + 自我停止
        @Suppress("DEPRECATION")
        stopForeground(true)
        stopSelf()

        VpnEventBus.postStatus("off")
        Log.i(TAG, "doStop: done")
    }

    // ── PlatformInterface（inner class，持有 VpnService.Builder 访问权）────

    /**
     * sing-box libbox 平台回调实现。
     *
     * [openTun]: 用 TunOptions 配置 VpnService.Builder，调用 establish() 取 TUN fd。
     * [autoDetectInterfaceControl]: 调用 VpnService.protect(fd) 防止 TUN 流量形成环路。
     */
    private inner class PangolinPlatformInterface : PlatformInterface {

        /**
         * libbox 回调 — 创建 TUN 接口并返回 fd。
         *
         * 注：TunOptions 的具体 getter 方法名（inet4Address / mtu 等）
         *   需与实际 libbox.aar 中 go.libbox.TunOptions 的 javadoc 对齐。
         *   若方法不存在，请使用 options 对应的实际方法名替换。
         */
        override fun openTun(options: TunOptions): Int {
            val builder = Builder()  // VpnService.Builder（inner class，此处可直接调用）

            // ── 地址配置 ──────────────────────────────────────────
            // libbox API: TunOptions.inet4Address() 返回 CIDR 字符串（如 "172.19.0.1/30"）
            // 若 API 名不同（如 getInet4Address()）请对应修改
            val inet4 = tryGetInet4(options)
            if (inet4.isNotEmpty()) {
                val slash = inet4.indexOf('/')
                if (slash > 0) {
                    builder.addAddress(inet4.substring(0, slash), inet4.substring(slash + 1).toInt())
                } else {
                    builder.addAddress(inet4, 30)
                }
            } else {
                // 兜底：使用 PoC 静态配置的地址
                builder.addAddress("172.19.0.1", 30)
            }

            val inet6 = tryGetInet6(options)
            if (inet6.isNotEmpty()) {
                val slash = inet6.indexOf('/')
                if (slash > 0) {
                    builder.addAddress(inet6.substring(0, slash), inet6.substring(slash + 1).toInt())
                }
            }

            // ── MTU ────────────────────────────────────────────────
            val mtu = tryGetMtu(options)
            builder.setMtu(mtu)

            // ── 路由：将所有流量导入 TUN（sing-box auto_route = true）────
            builder.addRoute("0.0.0.0", 0)   // IPv4 全部流量
            builder.addRoute("::", 0)          // IPv6 全部流量

            // ── DNS：sing-box 内置 Fake-IP 地址 ───────────────────
            // sing-box 在 172.18.0.0/15 运行 Fake-IP，设置匹配 DNS 服务器
            builder.addDnsServer("198.18.0.2")

            builder.setSession("Pangolin")
            builder.setBlocking(true)

            val pfd = builder.establish()
                ?: throw Exception("VpnService.Builder.establish() returned null — 可能未授权")
            currentTunPfd = pfd

            Log.i(TAG, "openTun: fd=${pfd.fd}, inet4=$inet4, mtu=$mtu")
            return pfd.fd
        }

        /**
         * libbox 回调 — 调用 VpnService.protect() 使 fd 跳过 TUN，防止路由环路。
         *
         * 注：gomobile 将 Go `error` 返回翻译为抛出 Exception（无返回值）。
         */
        override fun autoDetectInterfaceControl(fd: Int) {
            if (!protect(fd)) {
                throw Exception("VpnService.protect(fd=$fd) failed")
            }
        }

        override fun writeLog(message: String) {
            Log.v("libbox", message)
        }

        /** 使用 Android VpnService 的 protect() 机制，不需要平台独立的路由探测 */
        override fun usePlatformAutoDetectInterfaceControl(): Boolean = true

        /** 使用 Android 系统的网络变化监听，不使用 libbox 内置监听 */
        override fun usePlatformDefaultInterfaceMonitor(): Boolean = false

        /** 不使用 libbox 内置接口枚举 */
        override fun usePlatformInterfaceGetter(): Boolean = false

        /**
         * 进程信息查询（用于 sing-box 的应用规则）。
         * PoC 阶段不需要，返回 null 即可（libbox 会跳过进程匹配规则）。
         */
        override fun findProcessInfo(
            networkType: Int,
            srcIP: String,
            srcPort: Int,
            destIP: String,
            destPort: Int,
        ): ProcessInfo? = null

        // ── TunOptions 安全 getter（兜底处理 API 名不匹配问题）──

        /** 安全读取 inet4Address，若 API 不匹配返回空串，由调用方用兜底值 */
        private fun tryGetInet4(options: TunOptions): String = try {
            // libbox API: options.inet4Address() — 若名称不同请修改
            options.inet4Address()
        } catch (e: Exception) {
            Log.w(TAG, "TunOptions.inet4Address() failed: $e")
            ""
        }

        private fun tryGetInet6(options: TunOptions): String = try {
            options.inet6Address()
        } catch (e: Exception) {
            ""
        }

        private fun tryGetMtu(options: TunOptions): Int = try {
            // libbox API: options.mtu() returns Int32/Int
            options.mtu().toInt().coerceIn(576, 65535)
        } catch (e: Exception) {
            9000  // 兜底：与 PoC 配置对齐
        }
    }

    // ── 通知 ─────────────────────────────────────────────────────

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                "穿山甲加速",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "穿山甲加速状态"
                setShowBadge(false)
            }
            getSystemService(NotificationManager::class.java)
                .createNotificationChannel(channel)
        }
    }

    private fun updateNotification(contentText: String) {
        getSystemService(NotificationManager::class.java)
            .notify(NOTIFICATION_ID, buildNotification(contentText))
    }

    private fun buildNotification(contentText: String): Notification {
        val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        } else {
            @Suppress("DEPRECATION")
            PendingIntent.FLAG_UPDATE_CURRENT
        }
        val openIntent = PendingIntent.getActivity(
            this, 0,
            Intent(this, MainActivity::class.java),
            pendingFlags
        )

        val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
        } else {
            @Suppress("DEPRECATION")
            Notification.Builder(this)
        }

        return builder
            .setContentTitle("穿山甲")
            .setContentText(contentText)   // "加速已开启" / "正在连接…" / "连接失败"
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setContentIntent(openIntent)
            .setOngoing(true)
            .build()
    }
}
