package com.example.modifier.service

import android.accessibilityservice.AccessibilityService
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.PixelFormat
import android.os.Build
import android.util.DisplayMetrics
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.GONE
import android.view.View.OnTouchListener
import android.view.View.VISIBLE
import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.widget.CompoundButton
import android.widget.FrameLayout
import androidx.annotation.MenuRes
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import com.example.modifier.utils.ADB
import com.example.modifier.BuildConfig
import com.example.modifier.R
import com.example.modifier.TraverseResult
import com.example.modifier.baseTag
import com.example.modifier.data.AppDatabase
import com.example.modifier.databinding.FloatingWindowBinding
import com.example.modifier.enums.ReqState
import com.example.modifier.http.downloadImage
import com.example.modifier.repo.AppPrefsRepo
import com.example.modifier.repo.AppStateRepo
import com.example.modifier.repo.BackupRepository
import com.example.modifier.repo.GmsgStateRepo
import com.example.modifier.repo.SpoofedInfoRepo
import com.example.modifier.utils.ROOT_ACCESS
import com.example.modifier.utils.checkPif
import com.example.modifier.utils.clearConv
import com.example.modifier.utils.findFirstByDesc
import com.example.modifier.utils.hasRootAccess
import com.example.modifier.utils.killPhoneProcess
import com.example.modifier.utils.optimize
import com.example.modifier.utils.restartSelf
import com.example.modifier.utils.setBatteryLevel
import com.example.modifier.utils.syncTime
import com.google.android.material.color.DynamicColors
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URL
import java.util.Timer
import java.util.TimerTask
import java.util.concurrent.atomic.AtomicReference
import kotlin.math.max
import kotlin.math.min
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds


@SuppressLint("SetTextI18n")
class ModifierService : AccessibilityService() {
    @SuppressLint("StaticFieldLeak")
    companion object {
        private const val TAG = "$baseTag/Service"
        const val NAME: String = BuildConfig.APPLICATION_ID + ".service.ModifierService"
        var instance: ModifierService? = null
    }

    private lateinit var binding: FloatingWindowBinding

    private val backupItemDao by lazy { AppDatabase.getDatabase(this).itemDao() }
    private val appPrefsRepo = AppPrefsRepo.instance
    private val appStateRepo = AppStateRepo.instance
    private val gmsgStateRepo = GmsgStateRepo.instance
    private val screenInspector by lazy { ScreenInspector(this) }
    private val screenController by lazy { ScreenController(this, screenInspector) }
    private val spoofedInfoRepo = SpoofedInfoRepo.instance
    private val backupRepository by lazy { BackupRepository(this, backupItemDao) }
    private lateinit var socketClient: SocketClient
    lateinit var taskRunner: TaskRunner
    private lateinit var uiContext: Context

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
//        Log.w(TAG, "AccessibilityEvent: type=${event.eventType} text=${event.text}")
        if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (event.text.any { it.contains("允许 USB 调试吗？") || it.contains("Allow USB debugging?") }) {
                CoroutineScope(Dispatchers.IO).launch {
                    screenController.allowDebugging()
                }
            }
        }
    }

    override fun onInterrupt() {
        Log.e(TAG, "onInterrupt")
    }

    @OptIn(FlowPreview::class)
    @SuppressLint("ClickableViewAccessibility")
    override fun onServiceConnected() {
        super.onServiceConnected()

        CoroutineScope(Dispatchers.IO).launch {
            taskRunner = TaskRunner(
                this@ModifierService,
                screenInspector,
                screenController,
                appStateRepo,
                appPrefsRepo,
                spoofedInfoRepo,
                gmsgStateRepo,
                backupRepository,
            )
            launch {
                appStateRepo.appState.debounce(200).collect {
                    if (this@ModifierService::socketClient.isInitialized) {
                        socketClient.reportDeviceStatues()
                    }
                }
            }
            launch {
                appStateRepo.appState.collect {
                    withContext(Dispatchers.Main) {
                        binding.swSend.isChecked = it.send
                        binding.btnReq.isEnabled = it.reqState == ReqState.NONE
                        binding.tvCount.text = "${it.singleSuccess} / ${it.singleExecuted}"
                        if (it.suspended) {
                            binding.btnReq.backgroundTintList = ContextCompat.getColorStateList(
                                binding.root.context,
                                R.color.btn_color_error
                            )
                        } else {
                            binding.btnReq.backgroundTintList = ContextCompat.getColorStateList(
                                binding.root.context,
                                R.color.btn_color
                            )
                        }
                        when (it.reqState) {
                            ReqState.NONE -> {
                                binding.tvStatus.visibility = GONE
                                binding.tvStatus.text = ""
                            }

                            ReqState.CLEAN -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "cleaning messages"
                            }

                            ReqState.RESET -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "resetting GMS"
                            }

                            ReqState.BACKUP -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "backing up"
                            }

                            ReqState.RESTORE -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "restoring"
                            }

                            ReqState.REQUEST -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "requesting number"
                            }

                            ReqState.OTP_1 -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "waiting for OTP sent"
                            }

                            ReqState.OTP_2 -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "waiting for OTP received"
                            }

                            ReqState.CONFIG -> {
                                binding.tvStatus.visibility = VISIBLE
                                binding.tvStatus.text = "waiting for configuration"
                            }
                        }
                    }
                }
            }
            launch {
                appPrefsRepo.appPrefs.collect {
                    withContext(Dispatchers.Main) {
                        binding.swSend.text = it.name
                    }
                    if (this@ModifierService::socketClient.isInitialized) {
                        socketClient.disconnect()
                    }
                    socketClient = SocketClient(taskRunner)

                }
            }
            launch {
                spoofedInfoRepo.spoofedInfo.collect {
                    withContext(Dispatchers.Main) {
                        binding.tvCountry.text = it.country
                    }
                }
            }
            launch {
                gmsgStateRepo.logs.debounce(200).collect {
                    withContext(Dispatchers.Main) {
                        binding.tvLog.text = it
                        delay(100)
                        binding.scroll.fullScroll(View.FOCUS_DOWN)
                    }
                }
            }

            appStateRepo.updateRuntimeFlags(preparing = true)
            if (hasRootAccess()) {
                ROOT_ACCESS = true
            } else {
                if (!ADB.connect()) {
                    binding.tvLog.text = "ADB connection failed"
                    return@launch
                }
            }
            launch {
                gmsgStateRepo.startLogging()
            }
            delay(15.seconds)
            appPrefsRepo.updateId()
            optimize()
            syncTime()
            if (ROOT_ACCESS) {
                checkPif()
                if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
                    killPhoneProcess(force = false)
                    delay(15.seconds)
                }
            }
            appStateRepo.updateRuntimeFlags(preparing = false)

            val timer = Timer()
            timer.schedule(object : TimerTask() {
                override fun run() {
                    CoroutineScope(Dispatchers.IO).launch {
                        try {
                            syncTime()
                            if (Build.MODEL.startsWith("SM-F707") || Build.MODEL.startsWith("SM-F711")) {
                                setBatteryLevel(100)
                            }
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                    }
                }
            }, 0, 5.minutes.inWholeMilliseconds)
        }

        instance = this

        val displayMetrics = DisplayMetrics()
        val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        windowManager.defaultDisplay.getMetrics(displayMetrics)
        val height = displayMetrics.heightPixels
        val width = displayMetrics.widthPixels

        val mLayout = FrameLayout(this)
        val layoutParams = WindowManager.LayoutParams()
        layoutParams.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
        layoutParams.format = PixelFormat.TRANSLUCENT
        layoutParams.flags = layoutParams.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        layoutParams.flags = layoutParams.flags or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
        layoutParams.x = 0
        layoutParams.y = 800
        layoutParams.gravity = Gravity.START or Gravity.TOP

        uiContext = DynamicColors.wrapContextIfAvailable(applicationContext, R.style.AppTheme)
        val inflater = LayoutInflater.from(uiContext)
        binding = FloatingWindowBinding.inflate(inflater, mLayout, true)
        binding.tvVersion.text = "v${BuildConfig.VERSION_CODE}"
        windowManager.addView(mLayout, layoutParams)


        var maxX = 0
        var maxY = 0
        binding.root.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY)
        binding.root.post {
            maxX = width - binding.root.measuredWidth
            maxY = height - binding.root.measuredHeight
            layoutParams.x = maxX
            windowManager.updateViewLayout(mLayout, layoutParams)
        }

        val downX = AtomicReference(0f)
        val downY = AtomicReference(0f)
        val downParamX = AtomicReference(0)
        val downParamY = AtomicReference(0)

        val touchListener = OnTouchListener { v, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    downX.set(event.rawX)
                    downY.set(event.rawY)
                    downParamX.set(layoutParams.x)
                    downParamY.set(layoutParams.y)
                }

                MotionEvent.ACTION_MOVE -> {
                    layoutParams.x = min(
                        max((downParamX.get() + (event.rawX - downX.get())).toDouble(), 0.0),
                        maxX.toDouble()
                    )
                        .toInt()
                    layoutParams.y = min(
                        max((downParamY.get() + (event.rawY - downY.get())).toDouble(), 0.0),
                        maxY.toDouble()
                    )
                        .toInt()
                    windowManager.updateViewLayout(mLayout, layoutParams)
                }

                MotionEvent.ACTION_UP -> {
                    return@OnTouchListener event.eventTime - event.downTime >= 200
                }
            }
            false
        }

        binding.swSend.setOnTouchListener(touchListener)
        binding.swSend.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean ->
            CoroutineScope(Dispatchers.IO).launch {
                appStateRepo.updateSend(isChecked)
            }
        }

        binding.btnReq.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                taskRunner.requestNumberOnTask(reset = true, noBackup = true)
            }
        }

        binding.btnReset.setOnClickListener {
            if (appStateRepo.appState.value.reqState != ReqState.NONE) {
                return@setOnClickListener
            }
            binding.btnReset.isEnabled = false
            CoroutineScope(Dispatchers.IO).launch {
                taskRunner.reset()
                withContext(Dispatchers.Main) {
                    binding.btnReset.isEnabled = true
                }
            }
        }

        binding.btnMore.setOnClickListener {
            showMenu(uiContext, binding.btnMore, R.menu.more)
        }
    }

    private fun showMenu(context: Context, v: View, @MenuRes menuRes: Int) {
        val popup = PopupMenu(context, v)
        popup.menuInflater.inflate(menuRes, popup.menu)
        popup.menu.findItem(R.id.store_numbers).isVisible =
            !appStateRepo.appState.value.storing
        popup.menu.findItem(R.id.cancel_store_numbers).isVisible =
            appStateRepo.appState.value.storing
        popup.menu.findItem(R.id.check_availability).isEnabled =
            !appStateRepo.appState.value.checkingConnection
        if (BuildConfig.DEBUG) {
            popup.menu.findItem(R.id.inspect).isVisible = true
            popup.menu.findItem(R.id.toggle_on).isVisible = true
            popup.menu.findItem(R.id.toggle_off).isVisible = true
        }

        popup.setOnMenuItemClickListener { item ->
            CoroutineScope(Dispatchers.IO).launch {
                when (item.itemId) {
                    R.id.inspect -> {
                        delay(1500)
                        screenInspector.traverseNode(TraverseResult(), true)
                        rootInActiveWindow.findFirstByDesc(
                            Regex(
                                "wireless debugging",
                                RegexOption.IGNORE_CASE
                            )
                        )
                    }

                    R.id.check_availability -> {
                        appStateRepo.updateRuntimeFlags(checkingConnection = true)
                        taskRunner.checkRcsA10y()
                        appStateRepo.updateRuntimeFlags(checkingConnection = false)
                    }

                    R.id.toggle_on -> {
                        screenController.toggleRcsSwitch(true)
                    }

                    R.id.toggle_off -> {
                        screenController.toggleRcsSwitch(false)
                    }

                    R.id.clear_conv -> {
                        clearConv()
                        appStateRepo.resetExecutedNum()
                    }

                    R.id.store_numbers -> {
                        taskRunner.storeNumbers()
                    }

                    R.id.cancel_store_numbers -> {
                        taskRunner.cancelStoreNumber()
                    }

                    R.id.change_profile -> {

                    }

                    R.id.restart_modifier -> {
                        restartSelf()
                    }

                    R.id.reset_counter -> {
                        CoroutineScope(Dispatchers.IO).launch {
                            appStateRepo.resetExecutedNum()
                            appStateRepo.resetSuccessNum()
                            appStateRepo.resetRequestedNum()

                            appStateRepo.resetGroupExecutedNum()
                            appStateRepo.resetGroupSuccessNum()
                        }
                    }

                    R.id.create_group -> {
                        delay(1500)
                        screenController.createGroup(
                            arrayOf(
                                "+160127494771",
                                "+150797845191",
                            ), 5000
                        )
                    }

                    R.id.group_send -> {
                        delay(1500)
                        if (screenController.groupSend(
                                "Hello World!",
                                downloadImage("https://nebuai.oss-cn-hangzhou.aliyuncs.com/image/20250313/iai4ntok.png")
                            )
                        ) {
                            Log.i(TAG, "showMenu: group send success")
                            delay(5000)
                            if (screenController.leaveGroup()) {
                                Log.i(TAG, "showMenu: leave group success")
                            } else {
                                Log.i(TAG, "showMenu: leave group failed")
                            }
                        } else {
                            Log.e(TAG, "showMenu: group send failed")
                        }
                    }
                }
            }
            true
        }
        popup.setOnDismissListener {
            // Respond to popup being dismissed.
        }
        // Show the popup menu.
        popup.show()
    }


}
