package com.hiddify.hiddify.bg

import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.os.PowerManager
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import com.hiddify.hiddify.Application
import com.hiddify.hiddify.R
import com.hiddify.hiddify.Settings
import com.hiddify.hiddify.constant.Action
import com.hiddify.hiddify.constant.Alert
import com.hiddify.hiddify.constant.Status
import go.Seq
import io.nekohasekai.libbox.BoxService
import io.nekohasekai.libbox.CommandServer
import io.nekohasekai.libbox.CommandServerHandler
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.libbox.PlatformInterface
import io.nekohasekai.libbox.SystemProxyStatus
import io.nekohasekai.mobile.Mobile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File

class BoxService(
        private val service: Service,
        private val platformInterface: PlatformInterface
) : CommandServerHandler {

    companion object {
        private const val TAG = "A/BoxService"

        private var initializeOnce = false
        private lateinit var workingDir: File
        private fun initialize() {
            if (initializeOnce) return
            val baseDir = Application.application.filesDir
            
            baseDir.mkdirs()
            workingDir = Application.application.getExternalFilesDir(null) ?: return
            workingDir.mkdirs()
            val tempDir = Application.application.cacheDir
            tempDir.mkdirs()
            Log.d(TAG, "base dir: ${baseDir.path}")
            Log.d(TAG, "working dir: ${workingDir.path}")
            Log.d(TAG, "temp dir: ${tempDir.path}")
            
            Mobile.setup(baseDir.path, workingDir.path, tempDir.path, false)
            Libbox.redirectStderr(File(workingDir, "stderr.log").path)
            initializeOnce = true
            return
        }

        fun parseConfig(path: String, tempPath: String, debug: Boolean): String {
            return try {
                Mobile.parse(path, tempPath, debug)
                ""
            } catch (e: Exception) {
                Log.w(TAG, e)
                e.message ?: "invalid config"
            }
        }

        fun buildConfig(path: String, options: String): String {
            return Mobile.buildConfig(path, options)
        }

        fun start() {
            val intent = runBlocking {
                withContext(Dispatchers.IO) {
                    Intent(Application.application, Settings.serviceClass())
                }
            }
            ContextCompat.startForegroundService(Application.application, intent)
        }

        fun stop() {
            Application.application.sendBroadcast(
                    Intent(Action.SERVICE_CLOSE).setPackage(
                            Application.application.packageName
                    )
            )
        }

        fun reload() {
            Application.application.sendBroadcast(
                    Intent(Action.SERVICE_RELOAD).setPackage(
                            Application.application.packageName
                    )
            )
        }
    }

    var fileDescriptor: ParcelFileDescriptor? = null

    private val status = MutableLiveData(Status.Stopped)
    private val binder = ServiceBinder(status)
    private val notification = ServiceNotification(status, service)
    private var boxService: BoxService? = null
    private var commandServer: CommandServer? = null
    private var receiverRegistered = false
    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            when (intent.action) {
                Action.SERVICE_CLOSE -> {
                    stopService()
                }

                Action.SERVICE_RELOAD -> {
                    serviceReload()
                }

                PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED -> {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        serviceUpdateIdleMode()
                    }
                }
            }
        }
    }

    private fun startCommandServer() {
        val commandServer =
                CommandServer(this, 300)
        commandServer.start()
        this.commandServer = commandServer
    }

    private var activeProfileName = ""
    private suspend fun startService(delayStart: Boolean = false) {
        try {
            Log.d(TAG, "🚀 [步骤1] 开始启动服务")

            val selectedConfigPath = Settings.activeConfigPath
            Log.d(TAG, "📂 [步骤2] 配置路径: $selectedConfigPath")
            if (selectedConfigPath.isBlank()) {
                Log.e(TAG, "❌ [步骤2] 配置路径为空!")
                stopAndAlert(Alert.EmptyConfiguration)
                return
            }

            activeProfileName = Settings.activeProfileName
            Log.d(TAG, "📝 [步骤3] Profile名称: $activeProfileName")

            val configOptions = Settings.configOptions
            Log.d(TAG, "⚙️ [步骤4] 配置选项长度: ${configOptions.length}")
            if (configOptions.isBlank()) {
                Log.e(TAG, "❌ [步骤4] 配置选项为空!")
                stopAndAlert(Alert.EmptyConfiguration)
                return
            }

            Log.d(TAG, "🔨 [步骤5] 开始构建配置...")
            val content = try {
                Mobile.buildConfig(selectedConfigPath, configOptions)
            } catch (e: Exception) {
                Log.e(TAG, "❌ [步骤5] buildConfig失败: ${e.message}", e)
                Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
                stopAndAlert(Alert.EmptyConfiguration)
                return
            }
            Log.d(TAG, "✅ [步骤5] 配置构建成功, 内容长度: ${content.length}")

            File(workingDir, "current-config.json").writeText(content)
            Log.d(TAG, "💾 [步骤6] 配置已保存")

            Log.d(TAG, "🔔 [步骤7] 显示通知...")
            withContext(Dispatchers.Main) {
                notification.show(activeProfileName, R.string.status_starting)
                binder.broadcast {
                    it.onServiceResetLogs(listOf())
                }
            }

            Log.d(TAG, "🌐 [步骤8] 启动网络监听...")
            DefaultNetworkMonitor.start()
            Libbox.registerLocalDNSTransport(LocalResolver)
            Libbox.setMemoryLimit(!Settings.disableMemoryLimit)
            Log.d(TAG, "✅ [步骤8] 网络监听已启动")

            Log.d(TAG, "🎯 [步骤9] 调用 Libbox.newService()...")
            val newService = try {
                Libbox.newService(content, platformInterface)
            } catch (e: Exception) {
                Log.e(TAG, "❌ [步骤9] Libbox.newService() 失败!")
                Log.e(TAG, "❌ 错误信息: ${e.message}")
                Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
                Log.e(TAG, "❌ 堆栈跟踪: ${e.stackTraceToString()}")
                stopAndAlert(Alert.CreateService, e.message)
                return
            }
            Log.d(TAG, "✅ [步骤9] Libbox.newService() 成功!")

            if (delayStart) {
                Log.d(TAG, "⏳ [步骤10] 延迟1秒...")
                delay(1000L)
            }

            Log.d(TAG, "▶️ [步骤11] 调用 newService.start()...")
            try {
                newService.start()
                Log.d(TAG, "✅ [步骤11] newService.start() 成功!")
            } catch (e: Exception) {
                Log.e(TAG, "❌ [步骤11] newService.start() 失败!")
                Log.e(TAG, "❌ 错误信息: ${e.message}")
                Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
                Log.e(TAG, "❌ 堆栈跟踪: ${e.stackTraceToString()}")
                throw e
            }

            boxService = newService
            commandServer?.setService(boxService)
            status.postValue(Status.Started)
            Log.d(TAG, "📊 [步骤12] 状态已设置为 Started")

            withContext(Dispatchers.Main) {
                notification.show(activeProfileName, R.string.status_started)
            }
            notification.start()
            Log.d(TAG, "🎉 [完成] 服务启动成功!")
        } catch (e: Exception) {
            Log.e(TAG, "❌ [致命错误] startService异常!")
            Log.e(TAG, "❌ 错误信息: ${e.message}")
            Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
            Log.e(TAG, "❌ 堆栈跟踪: ${e.stackTraceToString()}")
            stopAndAlert(Alert.StartService, e.message)
            return
        }
    }

    override fun serviceReload() {
        notification.close()
        status.postValue(Status.Starting)
        val pfd = fileDescriptor
        if (pfd != null) {
            pfd.close()
            fileDescriptor = null
        }
        commandServer?.setService(null)
        boxService?.apply {
            runCatching {
                close()
            }.onFailure {
                writeLog("service: error when closing: $it")
            }
            Seq.destroyRef(refnum)
        }
        boxService = null
        runBlocking {
            startService(true)
        }
    }

    override fun getSystemProxyStatus(): SystemProxyStatus {
        val status = SystemProxyStatus()
        if (service is VPNService) {
            status.available = service.systemProxyAvailable
            status.enabled = service.systemProxyEnabled
        }
        return status
    }

    override fun setSystemProxyEnabled(isEnabled: Boolean) {
        serviceReload()
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun serviceUpdateIdleMode() {
        if (Application.powerManager.isDeviceIdleMode) {
            boxService?.pause()
        } else {
            boxService?.wake()
        }
    }

    private fun stopService() {
        if (status.value != Status.Started) return
        status.value = Status.Stopping
        if (receiverRegistered) {
            service.unregisterReceiver(receiver)
            receiverRegistered = false
        }
        notification.close()
        GlobalScope.launch(Dispatchers.IO) {
            val pfd = fileDescriptor
            if (pfd != null) {
                pfd.close()
                fileDescriptor = null
            }
            commandServer?.setService(null)
            boxService?.apply {
                runCatching {
                    close()
                }.onFailure {
                    writeLog("service: error when closing: $it")
                }
                Seq.destroyRef(refnum)
            }
            boxService = null
            Libbox.registerLocalDNSTransport(null)
            DefaultNetworkMonitor.stop()

            commandServer?.apply {
                close()
                Seq.destroyRef(refnum)
            }
            commandServer = null
            Settings.startedByUser = false
            withContext(Dispatchers.Main) {
                status.value = Status.Stopped
                service.stopSelf()
            }
        }
    }
    override fun postServiceClose() {
        // Not used on Android
    }

    private suspend fun stopAndAlert(type: Alert, message: String? = null) {
        Settings.startedByUser = false
        withContext(Dispatchers.Main) {
            if (receiverRegistered) {
                service.unregisterReceiver(receiver)
                receiverRegistered = false
            }
            notification.close()
            binder.broadcast { callback ->
                callback.onServiceAlert(type.ordinal, message)
            }
            status.value = Status.Stopped
        }
    }

    fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (status.value != Status.Stopped) return Service.START_NOT_STICKY
        status.value = Status.Starting

        if (!receiverRegistered) {
            ContextCompat.registerReceiver(service, receiver, IntentFilter().apply {
                addAction(Action.SERVICE_CLOSE)
                addAction(Action.SERVICE_RELOAD)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
                }
            }, ContextCompat.RECEIVER_NOT_EXPORTED)
            receiverRegistered = true
        }

        GlobalScope.launch(Dispatchers.IO) {
            Settings.startedByUser = true
            initialize()
            try {
                startCommandServer()
            } catch (e: Exception) {
                // CommandServer 启动失败不是致命错误
                // 在 Android 12+ SELinux 环境下,chown 操作可能被拒绝
                // 但这不影响 VPN 核心功能,因此记录警告但继续启动服务
                Log.w(TAG, "CommandServer failed to start (non-fatal): ${e.message}")
            }
            startService()
        }
        return Service.START_NOT_STICKY
    }

    fun onBind(intent: Intent): IBinder {
        return binder
    }

    fun onDestroy() {
        binder.close()
    }

    fun onRevoke() {
        stopService()
    }

    fun onTaskRemoved(intent: Intent?) {
        Log.d(TAG, "📦 onTaskRemoved 被调用, 准备停止 VPN 服务")
        stopService()
    }

    fun writeLog(message: String) {
        binder.broadcast {
            it.onServiceWriteLog(message)
        }
    }

}