/*
 * Hermes Node Android
 * Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package net.nexlab.hermesnodeandroid

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.graphics.Path
import android.os.Build
import android.os.Bundle
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class HermesAccessibilityService : AccessibilityService() {
    override fun onServiceConnected() {
        instance = this
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
        lastPackageName = event?.packageName?.toString() ?: lastPackageName
        lastClassName = event?.className?.toString() ?: lastClassName
    }

    override fun onInterrupt() = Unit

    override fun onDestroy() {
        if (instance === this) instance = null
        super.onDestroy()
    }

    fun activeWindowInfo(): Map<String, Any?> {
        val root = rootInActiveWindow
        return mapOf(
            "package" to (root?.packageName?.toString() ?: lastPackageName),
            "class" to (root?.className?.toString() ?: lastClassName),
            "text" to root?.text?.toString(),
            "content_description" to root?.contentDescription?.toString(),
            "source" to if (root != null) "accessibility_root" else "last_accessibility_event",
        )
    }

    fun click(x: Int, y: Int, durationMs: Long = 60L): Boolean = gesture {
        val p = Path().apply { moveTo(x.toFloat(), y.toFloat()) }
        addStroke(GestureDescription.StrokeDescription(p, 0, durationMs))
    }

    fun swipe(x1: Int, y1: Int, x2: Int, y2: Int, durationMs: Long = 300L): Boolean = gesture {
        val p = Path().apply {
            moveTo(x1.toFloat(), y1.toFloat())
            lineTo(x2.toFloat(), y2.toFloat())
        }
        addStroke(GestureDescription.StrokeDescription(p, 0, durationMs))
    }

    fun longPress(x: Int, y: Int, durationMs: Long = 700L): Boolean = click(x, y, durationMs)

    fun globalAction(action: Int): Boolean = performGlobalAction(action)

    fun typeText(text: String): Boolean {
        val node = rootInActiveWindow?.findFocus(AccessibilityNodeInfo.FOCUS_INPUT) ?: return false
        val args = Bundle().apply { putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text) }
        return node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)
    }

    private fun gesture(build: GestureDescription.Builder.() -> Unit): Boolean {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false
        val latch = CountDownLatch(1)
        var ok = false
        val desc = GestureDescription.Builder().apply(build).build()
        dispatchGesture(desc, object : GestureResultCallback() {
            override fun onCompleted(gestureDescription: GestureDescription?) {
                ok = true
                latch.countDown()
            }
            override fun onCancelled(gestureDescription: GestureDescription?) {
                ok = false
                latch.countDown()
            }
        }, null)
        latch.await(2, TimeUnit.SECONDS)
        return ok
    }

    companion object {
        @Volatile private var instance: HermesAccessibilityService? = null
        @Volatile private var lastPackageName: String? = null
        @Volatile private var lastClassName: String? = null

        fun current(): HermesAccessibilityService? = instance
    }
}
