package com.flashlink.vpn

import android.app.Notification
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.os.PowerManager
import android.os.SystemClock
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.flashlink.vpn.App.Companion.EXTRA_IS_FOREGROUND
import com.flashlink.vpn.XRayApi.Companion.VPN_STATE_CONNECTED
import com.flashlink.vpn.XRayApi.Companion.VPN_STATE_ERROR
import com.flashlink.vpn.XRayApi.Companion.VPN_STATE_IDLE
import com.google.gson.Gson
import go.Seq
import flashlink_mobile.FlashLink_mobile
import flashlink_mobile.ProxyConnectorHandler
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.json.JSONObject
import win.fkey.netboost.service.NetworkReporter
import java.io.File


class XRayService : LifecycleVpnService() {
    private val tunnelService = TProxyService()
    private var tunFd: ParcelFileDescriptor? = null
    private val mutex = Mutex()

    @Volatile
    private var vpnStatus = VPN_STATE_IDLE

    // 计时功能相关变量
    private var timerJob: Job? = null
    private var timerMode = TIMER_MODE_NORMAL
    private var timerBaseRealtime = 0L
    private var timerInitialTime = 0L
    private var peekElapsedSeconds = 0L

    private var xrayConfig: XrayConfig? = null
    private var remainTime = 0L

    // 屏幕状态跟踪
    @Volatile
    private var isScreenOn = true
    private var screenReceiver: BroadcastReceiver? = null

    // 应用前后台状态跟踪
    @Volatile
    private var isAppForeground = true
    private var foregroundReceiver: BroadcastReceiver? = null

    // 服务状态（实例级别）
    private var serviceStatus: ServiceStatus = ServiceStatus.Disconnected

    companion object {
        private val TAG: String = XRayService::class.java.simpleName

        private const val FOREGROUND_SERVICE_ID: Int = 1
        private const val NOTIFICATION_CHANNEL_ID: String = "flashlink"

        // 计时模式常量
        const val TIMER_MODE_NORMAL = 0  // 普通计时（正计时）
        const val TIMER_MODE_COUNTDOWN = 1  // 倒计时

        // VPN 网络配置常量
        private const val TUN_IP_ADDRESS = "192.168.34.2"
        private const val TUN_PREFIX_LENGTH = 24
        private const val TUN_MTU = 1500
        private const val DNS_SERVER = "8.8.8.8"
        private const val VPN_SESSION_NAME = "ix_vpn"
    }

    override fun onCreate() {
        super.onCreate()
        // 初始化文件日志系统
        VLog.init(applicationContext)
        VLog.i(TAG, "XRayService onCreate")

        Seq.setContext(applicationContext)
        FlashLink_mobile.initProxyConnector(0, getXrayLog())
        registerNotificationChannel(
            this,
            NOTIFICATION_CHANNEL_ID,
            "VPN",
        )
        // 注册屏幕状态监听
        registerScreenReceiver()
        // 注册应用前后台状态监听
        registerForegroundReceiver()
    }

    /**
     * 内核日志路径
     */
    private fun getXrayLog(): String {
        val logDir = File(applicationContext.filesDir, "xray")
        if (!logDir.exists()) {
            logDir.mkdirs()
        }
        VLog.i(TAG, logDir.absolutePath)
        return logDir.absolutePath
    }

    /**
     * 注册屏幕状态监听器
     */
    private fun registerScreenReceiver() {
        screenReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                when (intent.action) {
                    Intent.ACTION_SCREEN_ON -> {
                        isScreenOn = true
                        VLog.d(TAG, "屏幕亮起")
                    }

                    Intent.ACTION_SCREEN_OFF -> {
                        isScreenOn = false
                        VLog.d(TAG, "屏幕熄灭")
                    }
                }
            }
        }

        val filter = IntentFilter().apply {
            addAction(Intent.ACTION_SCREEN_ON)
            addAction(Intent.ACTION_SCREEN_OFF)
        }
        registerReceiver(screenReceiver, filter)

        // 获取初始屏幕状态
        val powerManager = getSystemService(POWER_SERVICE) as PowerManager
        isScreenOn = powerManager.isInteractive
    }

    /**
     * 注销屏幕状态监听器
     */
    private fun unregisterScreenReceiver() {
        screenReceiver?.let {
            try {
                unregisterReceiver(it)
            } catch (e: Exception) {
                VLog.e(TAG, "注销屏幕监听器失败", e)
            }
            screenReceiver = null
        }
    }

    /**
     * 注册应用前后台状态监听器
     */
    private fun registerForegroundReceiver() {
        foregroundReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == APP_FOREGROUND_BROADCAST) {
                    isAppForeground = intent.getBooleanExtra(EXTRA_IS_FOREGROUND, true)
                    VLog.d(TAG, "应用前后台状态变化: ${if (isAppForeground) "前台" else "后台"}")
                }
            }
        }

        val filter = IntentFilter(APP_FOREGROUND_BROADCAST)
        ContextCompat.registerReceiver(
            this,
            foregroundReceiver,
            filter,
            ContextCompat.RECEIVER_NOT_EXPORTED
        )
    }

    /**
     * 注销应用前后台状态监听器
     */
    private fun unregisterForegroundReceiver() {
        foregroundReceiver?.let {
            try {
                unregisterReceiver(it)
            } catch (e: Exception) {
                VLog.e(TAG, "注销前后台监听器失败", e)
            }
            foregroundReceiver = null
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        return when (val action = intent?.action) {
            START_ACTION -> {
                lifecycleScope.launch { intent.extras?.let { start(it) } }
                START_STICKY
            }

            STOP_ACTION -> {
                VLog.i(TAG, "onStartCommand")
                lifecycleScope.launch {
                    stop()
                }
                START_NOT_STICKY
            }

            else -> {
                VLog.i(TAG, "Unknown action: $action")
                START_NOT_STICKY
            }
        }
    }

    override fun onRevoke() {
        VLog.i(TAG, "onRevoke")
        lifecycleScope.launch { stop() }
        sendStatusMessage(VPN_STATE_ERROR, ERROR_REVOKE, "System cleanup")
    }

    override fun onDestroy() {
        VLog.i(TAG, "Service onDestroy")
        // 确保资源被清理
        // 注销屏幕状态监听
        unregisterScreenReceiver()
        // 注销前后台监听
        unregisterForegroundReceiver()
        // 停止计时
        stopTimer()
        // 先停止前台服务
        try {
            stopForeground(STOP_FOREGROUND_REMOVE)
        } catch (e: Exception) {
            VLog.e(TAG, "stopForeground 失败", e)
        }

        // 清理资源
        try {
            FlashLink_mobile.freeProxyConnector()
        } catch (e: Exception) {
            VLog.e(TAG, "freeProxyConnector 失败", e)
        }

        // 关闭日志系统
        VLog.close()
        super.onDestroy()
    }

    private suspend fun start(bundle: Bundle) {
        VLog.i(TAG, "Starting")

        if (serviceStatus == ServiceStatus.Connected) {
            VLog.i(TAG, "VPN already connected")
            return
        }

        val configJson = bundle.getString("config")
        xrayConfig = Gson().fromJson(configJson, XrayConfig::class.java)

        if (xrayConfig == null) {
            updateStatus(ServiceStatus.Failed)
            // 未启动tun，通知flutter并且调用stopSelf关闭服务
            sendStatusMessage(VPN_STATE_ERROR, ERROR_INIT, "参数异常")
            stopSelfService()
            return
        }

        remainTime = xrayConfig!!.remainTime * 1000L

        try {
            mutex.withLock {
                startTun2Socks()
            }
            updateStatus(ServiceStatus.Connected)
            startForeground()
        } catch (e: Exception) {
            VLog.e(TAG, "Failed to start VPN", e)
            updateStatus(ServiceStatus.Failed)
            stop()
            sendStatusMessage(VPN_STATE_ERROR, ERROR_INIT, "Failed to startTun2Socks")
        }
    }

    private fun startTun2Socks() {
        val config = xrayConfig ?: run {
            VLog.e(TAG, "xrayConfig is null")
            sendStatusMessage(VPN_STATE_ERROR, ERROR_INIT, "Config is null")
            stopSelfService()
            return
        }

        VLog.i(TAG, "socksPort: ${config.socksPort}, sessionId: ${config.sessionId}")
        VLog.i(TAG, "allowVpnApps count: ${config.allowVpnApps.size}, disallowVpnApps count: ${config.disallowVpnApps.size}")

        val builder = Builder().apply {
            setSession(VPN_SESSION_NAME)
            setMtu(TUN_MTU)
            addAddress(TUN_IP_ADDRESS, TUN_PREFIX_LENGTH)
            addRoute("0.0.0.0", 0)
            addDnsServer(DNS_SERVER)
        }

        // 配置应用白名单/黑名单
        if (config.allowVpnApps.isNotEmpty()) {
            config.allowVpnApps.forEach { app ->
                builder.addAllowedApplication(app)
            }
            VLog.i(TAG, "允许的应用: ${config.allowVpnApps.joinToString(", ")}")
        } else {
            config.disallowVpnApps.forEach { app ->
                builder.addDisallowedApplication(app)
            }
            builder.addDisallowedApplication(packageName)
            VLog.i(TAG, "禁止的应用: ${config.disallowVpnApps.joinToString(", ")}")
        }

        // 初始化网络请求参数
        NetworkReporter.getInstance().initialize(config)

        tunFd = builder.establish()
        tunFd?.let { tfd ->
            VLog.i(TAG, "VPN tunnel established, fd: ${tfd.fd}")
            tunnelService.startTunnel(this, config.socksPort, config.tunnelConfig, tfd.fd)
            FlashLink_mobile.proxyConnectorStart(config.sessionId, config.startOptions, vpnHandler)
            VLog.i(TAG, "XRay proxy started successfully")
        } ?: run {
            VLog.e(TAG, "Failed to establish VPN tunnel")
            updateStatus(ServiceStatus.Failed)
            sendStatusMessage(VPN_STATE_ERROR, ERROR_INIT, "Failed to establish VPN tunnel")
            stopSelfService()
        }
    }

    private val vpnHandler = ProxyConnectorHandler { status, params ->
        VLog.i(TAG, "status:$status params:$params")
        if (vpnStatus == status) {
            return@ProxyConnectorHandler
        }
        vpnStatus = status

        val (code, message) = parseStatusParams(params)

        when (status) {
            VPN_STATE_CONNECTED -> {
                val config = xrayConfig ?: return@ProxyConnectorHandler
                val mode = if (config.isCountdown) TIMER_MODE_COUNTDOWN else TIMER_MODE_NORMAL
                val time = if (config.isCountdown) remainTime else 0L
                startTimer(mode, time)
                uploadBoostResult(true, code)
                AppLogger.getInstance(this).updateAppJsonStatusInfo(true, code)
            }
            VPN_STATE_ERROR -> {
                AppLogger.getInstance(this).updateAppJsonStatusInfo(false, code)
                uploadBoostResult(false, code)
                VLog.i(TAG, "vpnHandler error")
                lifecycleScope.launch { stop() }
            }
        }

        sendStatusMessage(status, code, message)
    }

    private fun parseStatusParams(params: String): Pair<Long, String> {
        return try {
            val json = JSONObject(params)
            Pair(json.optLong("code", 0), json.optString("message", ""))
        } catch (e: Exception) {
            VLog.e(TAG, "解析 params JSON 失败", e)
            Pair(0L, "")
        }
    }

    private fun uploadBoostResult(success: Boolean, code: Long) {
        val config = xrayConfig ?: return
        try {
            val stats = queryStats(1) // QueryStatsConnectionHistory
            val reporter = NetworkReporter.getInstance()

            reporter.addBoostResultParams(
                "NM_BoostResult",
                stats,
                config.sessionId,
                success,
                code
            )

            val intent = Intent(BOOST_RESULT_BROADCAST).apply {
                setPackage(packageName)
                putExtra("param", reporter.buildRequestParams("NM_BoostResult"))
                putExtra("success", success)
                putExtra("locationCode", reporter.getLocationCode())
                putExtra("nodeId", reporter.getConnectedNodeId())
            }
            sendBroadcast(intent)
        } catch (e: Exception) {
            VLog.e(TAG, "uploadBoostResult error: ${e.message}")
        }
    }

    /**
     * 查询代理连接统计数据
     * @param types 查询类型: 1=连接历史, 2=带宽, 3=最大速度
     */
    private fun queryStats(vararg types: Byte): String? {
        return try {
            FlashLink_mobile.proxyConnectorQueryStats(types)
        } catch (e: Exception) {
            VLog.e(TAG, "queryStats error: ${e.message}")
            null
        }
    }

    private fun sendStatusMessage(status: Long, code: Long, message: String) {
        VLog.i(TAG, "sendStatusMessage status:$status code:$code message:$message")
        val intent = Intent(STATUS_BROADCAST).apply {
            setPackage(packageName)  // 必须设置包名，否则 RECEIVER_NOT_EXPORTED 接收不到
            putExtra("status", status)
            putExtra("code", code)
            putExtra("message", message)
        }
        sendBroadcast(intent)
    }

    private fun startForeground() {
        val notification: Notification = createNotification()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            startForeground(
                FOREGROUND_SERVICE_ID,
                notification,
                ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
            )
        } else {
            startForeground(FOREGROUND_SERVICE_ID, notification)
        }
    }

    private suspend fun stop() {
        VLog.i(TAG, "Stopping")

        mutex.withLock {
            stopTun2Socks()
        }
        stopSelfService()
    }

    private fun stopSelfService() {
        VLog.i(TAG, "stop self service")
        updateStatus(ServiceStatus.Disconnected)
        stopSelf()
    }

    private suspend fun stopTun2Socks() {
        VLog.i(TAG, "stopTun2Socks")
        // 停止计时
        stopTimer()

        dealStat()

        // 停止顺序优化：
        // 1. 先停止 proxy connector，切断数据源
        // 2. 再停止 tunnel（此时不再有新数据进入）
        // 3. 最后关闭 tun fd
        stopProxyConnector()
        stopTunnel()
        closeTunFd()

        VLog.i(TAG, "xray stopped")
    }

    private fun stopTunnel() {
        // 注意：必须先停止 tunnel，再关闭 fd
        // 否则 hev-socks5-tunnel 可能在读写 fd 时卡住
        try {
            // 使用单独线程执行 stopTunnel，避免阻塞，并添加超时
            val stopThread = Thread {
                try {
                    tunnelService.stopTunnel()
                    VLog.i(TAG, "Tunnel stopped")
                } catch (e: Exception) {
                    VLog.e(TAG, "停止 tunnel 失败", e)
                }
            }
            stopThread.start()
            // 等待最多 3 秒
            stopThread.join(3000)
            if (stopThread.isAlive) {
                VLog.w(TAG, "stopTunnel 超时，强制继续")
                stopThread.interrupt()
            }
        } catch (e: Exception) {
            VLog.e(TAG, "停止 tunnel 异常", e)
        }
    }

    private fun stopProxyConnector() {
        try {
            FlashLink_mobile.proxyConnectorStop()
            VLog.i(TAG, "Proxy connector stopped")
        } catch (e: Exception) {
            VLog.e(TAG, "停止 proxy connector 失败", e)
        }
    }

    private fun closeTunFd() {
        tunFd?.let { tfd ->
            try {
                tfd.close()
                VLog.i(TAG, "TUN file descriptor closed")
            } catch (e: Exception) {
                VLog.e(TAG, "关闭 TUN file descriptor 失败", e)
            }
            tunFd = null
        }
    }


    private fun updateStatus(newStatus: ServiceStatus) {
        VLog.d(TAG, "VPN status changed from $serviceStatus to $newStatus")
        serviceStatus = newStatus
    }

    private fun createNotification(): Notification =
        createConnectionNotification(
            this,
            NOTIFICATION_CHANNEL_ID,
            "FlashLink VPN",
            "Service is running",
        )

// ==================== 计时功能 ====================

    /**
     * 启动计时
     * @param mode 计时模式: TIMER_MODE_NORMAL(0)=普通计时, TIMER_MODE_COUNTDOWN(1)=倒计时
     * @param initialTime 初始时间（毫秒），普通计时为0，倒计时为总时长
     */
    private fun startTimer(mode: Int, initialTime: Long) {
        if (timerJob?.isActive == true) {
            VLog.w(TAG, "计时器已在运行")
            return
        }

        VLog.i(TAG, "启动计时: mode=$mode, initialTime=$initialTime")
        timerMode = mode
        timerInitialTime = initialTime
        timerBaseRealtime = SystemClock.elapsedRealtime()
        peekElapsedSeconds = 0L // 重置 Peek 计数器

        val peekInterval = xrayConfig?.peekTimeInterval ?: 0
        VLog.i(TAG, "Peek 上报间隔: ${peekInterval}秒")

        timerJob = lifecycleScope.launch {
            while (isActive) {
                // 计算从开始到现在经过的真实时间
                val elapsedTime = SystemClock.elapsedRealtime() - timerBaseRealtime

                val currentTime = if (timerMode == TIMER_MODE_NORMAL) {
                    // 普通计时：初始时间 + 经过时间
                    timerInitialTime + elapsedTime
                } else {
                    // 倒计时：初始时间 - 经过时间
                    timerInitialTime - elapsedTime
                }

                // 检查倒计时是否结束
                if (timerMode == TIMER_MODE_COUNTDOWN && currentTime <= 0) {
                    VLog.i(TAG, "倒计时结束 - 关闭VPN (elapsed: ${elapsedTime}ms)")
                    stop()
                    sendStatusMessage(VPN_STATE_ERROR, ERROR_REMAIN_TIME, "No available time")
                    return@launch
                }
                if (timerMode == TIMER_MODE_NORMAL && currentTime >= remainTime) {
                    VLog.i(TAG, "可用时间已结束 - 关闭VPN (elapsed: ${elapsedTime}ms)")
                    stop()
                    sendStatusMessage(VPN_STATE_ERROR, ERROR_REMAIN_TIME, "No available time")
                    return@launch
                }

                // Peek 定时上报（屏幕关闭时不上报，节省资源）
                peekElapsedSeconds++
                if (peekInterval > 0 && peekElapsedSeconds >= peekInterval) {
                    peekElapsedSeconds = 0L
                    if (isScreenOn) {
                        AppLogger.getInstance(this@XRayService).updateAppJsonStopTime()
                        callPeekInterface()
                    }
                }

                // 等待1秒
                delay(1000L)
            }
        }
    }

    /**
     * 停止计时
     */
    private fun stopTimer() {
        if (timerJob != null) {
            VLog.i(TAG, "停止计时")
        }
        timerJob?.cancel()
        timerJob = null
    }

    /**
     * 查询并记录统计日志
     */
    private fun dealStat() {
        val stats = queryStats(1, 2, 3) // 连接历史、带宽、最大速度
        val version = FlashLink_mobile.proxyConnectorVersion()
        VLog.i(TAG, "stats = $stats")

        // 将 stats 和 version 写入 core.json 日志文件
        CoreLogger.getInstance(this).updateStatsAndVersion(stats, version)
        // 更新停止时间
        AppLogger.getInstance(this).updateAppJsonStopTime()
    }

    /**
     * 调用 Peek 接口上报数据
     */
    private fun callPeekInterface() {
        val config = xrayConfig ?: return
        try {
            val stats = queryStats(3) // QueryStatsMaxSpeed
            VLog.i(TAG, "peek stats: $stats")

            val reporter = NetworkReporter.getInstance()
            reporter.addPeekLogParams("NM_PeekLog", stats, config.sessionId)
            reporter.report("/api/v1/event", "NM_PeekLog", null, null)

            VLog.i(TAG, "Peek log reported successfully")
        } catch (e: Exception) {
            VLog.e(TAG, "callPeekInterface error: ${e.message}")
        }
    }

    /**
     * 格式化时间显示
     */
    private fun formatTime(timeMs: Long): String {
        val totalSeconds = kotlin.math.abs(timeMs) / 1000
        val days = totalSeconds / 86400
        val hours = (totalSeconds % 86400) / 3600
        val minutes = (totalSeconds % 3600) / 60
        val seconds = totalSeconds % 60

        return when {
            days > 0 -> String.format("%d 天 %02d:%02d:%02d", days, hours, minutes, seconds)
            hours > 0 -> String.format("%02d:%02d:%02d", hours, minutes, seconds)
            else -> String.format("00:%02d:%02d", minutes, seconds)
        }
    }
}