package ai.z.chat;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.GestureDescription;
import android.graphics.Bitmap;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

public class AutoGLMService extends AccessibilityService {

    private static final String TAG = "AutoGLMService";
    private static volatile AutoGLMService instance;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        Log.i(TAG, "AutoGLM AccessibilityService created");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        instance = null;
        Log.i(TAG, "AutoGLM AccessibilityService destroyed");
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {}

    @Override
    public void onInterrupt() {}

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        instance = this;
        AccessibilityServiceInfo info = getServiceInfo();
        if (info == null) info = new AccessibilityServiceInfo();
        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        info.flags = AccessibilityServiceInfo.DEFAULT
                | AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
                | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS
                | AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
        info.notificationTimeout = 100;
        setServiceInfo(info);
        Log.i(TAG, "AutoGLM service connected and configured");
    }

    public static AutoGLMService getInstance() {
        return instance;
    }

    public static boolean isEnabled() {
        return instance != null;
    }

    public void tap(int x, int y) {
        GestureDescription gesture = buildClick(x, y, 50);
        if (gesture != null) dispatchGesture(gesture, null, null);
    }

    public void longPress(int x, int y) {
        GestureDescription gesture = buildClick(x, y, 500);
        if (gesture != null) dispatchGesture(gesture, null, null);
    }

    public void swipe(int startX, int startY, int endX, int endY, int durationMs) {
        GestureDescription gesture = buildSwipe(startX, startY, endX, endY, durationMs);
        if (gesture != null) dispatchGesture(gesture, null, null);
    }

    public void swipe(int startX, int startY, int endX, int endY) {
        swipe(startX, startY, endX, endY, 300);
    }

    public void typeText(String text) {
        AccessibilityNodeInfo focusNode = findFocusNode();
        if (focusNode != null) {
            Bundle args = new Bundle();
            args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
            focusNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
            focusNode.recycle();
        }
    }

    public void pressBack() {
        performGlobalAction(GLOBAL_ACTION_BACK);
    }

    public void pressHome() {
        performGlobalAction(GLOBAL_ACTION_HOME);
    }

    public void pressRecents() {
        performGlobalAction(GLOBAL_ACTION_RECENTS);
    }

    public void pressNotifications() {
        performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS);
    }

    public void pressQuickSettings() {
        performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS);
    }

    public void pressPowerDialog() {
        performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
    }

    public void pressLockScreen() {
        performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN);
    }

    public void takeScreenshot(String destPath) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            takeScreenshot(android.view.Display.DEFAULT_DISPLAY,
                getMainExecutor(), new TakeScreenshotCallback() {
                    @Override
                    public void onSuccess(ScreenshotResult screenshot) {
                        try {
                            Bitmap bitmap = Bitmap.wrapHardwareBuffer(
                                screenshot.getHardwareBuffer(), screenshot.getColorSpace());
                            if (bitmap != null) {
                                Bitmap softwareBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
                                try (FileOutputStream fos = new FileOutputStream(destPath)) {
                                    softwareBitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
                                }
                                softwareBitmap.recycle();
                            }
                            if (screenshot.getHardwareBuffer() != null) {
                                screenshot.getHardwareBuffer().close();
                            }
                        } catch (Exception e) {
                            Log.e(TAG, "Screenshot save failed", e);
                        }
                    }
                    @Override
                    public void onFailure(int errorCode) {
                        Log.e(TAG, "Screenshot failed: " + errorCode);
                    }
                });
        }
    }

    public String getUITree() {
        try {
            JSONObject root = new JSONObject();
            JSONArray windowsArr = new JSONArray();

            List<AccessibilityWindowInfo> windows = getWindows();
            for (AccessibilityWindowInfo window : windows) {
                JSONObject winObj = new JSONObject();
                winObj.put("id", window.getId());
                winObj.put("layer", window.getLayer());
                winObj.put("active", window.isActive());
                winObj.put("focused", window.isFocused());

                AccessibilityNodeInfo rootNode = window.getRoot();
                if (rootNode != null) {
                    winObj.put("root", serializeNode(rootNode));
                    rootNode.recycle();
                }
                windowsArr.put(winObj);
            }

            root.put("windows", windowsArr);
            root.put("windowCount", windows.size());
            return root.toString();
        } catch (Exception e) {
            Log.e(TAG, "getUITree failed", e);
            return "{}";
        }
    }

    public String getFocusedNodeInfo() {
        try {
            AccessibilityNodeInfo focusNode = findFocusNode();
            if (focusNode != null) {
                JSONObject obj = serializeNode(focusNode);
                focusNode.recycle();
                return obj.toString();
            }
        } catch (Exception e) {
            Log.w(TAG, "getFocusedNodeInfo failed", e);
        }
        return "{}";
    }

    public boolean clickNode(String viewId) {
        List<AccessibilityNodeInfo> nodes = findNodesByViewId(viewId);
        for (AccessibilityNodeInfo node : nodes) {
            if (node.isClickable()) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                node.recycle();
                return true;
            }
            AccessibilityNodeInfo clickable = findClickableAncestor(node);
            if (clickable != null) {
                clickable.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                clickable.recycle();
                node.recycle();
                return true;
            }
            node.recycle();
        }
        return false;
    }

    public boolean clickNodeByText(String text) {
        List<AccessibilityNodeInfo> nodes = findNodesByText(text);
        for (AccessibilityNodeInfo node : nodes) {
            if (node.isClickable()) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                node.recycle();
                return true;
            }
            AccessibilityNodeInfo clickable = findClickableAncestor(node);
            if (clickable != null) {
                clickable.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                clickable.recycle();
                node.recycle();
                return true;
            }
            node.recycle();
        }
        return false;
    }

    public boolean scrollNode(String viewId, int direction) {
        List<AccessibilityNodeInfo> nodes = findNodesByViewId(viewId);
        for (AccessibilityNodeInfo node : nodes) {
            boolean result = node.performAction(direction);
            node.recycle();
            if (result) return true;
        }
        return false;
    }

    public String getCurrentApp() {
        try {
            List<AccessibilityWindowInfo> windows = getWindows();
            for (AccessibilityWindowInfo window : windows) {
                if (window.isActive()) {
                    AccessibilityNodeInfo root = window.getRoot();
                    if (root != null) {
                        CharSequence pkg = root.getPackageName();
                        root.recycle();
                        return pkg != null ? pkg.toString() : "";
                    }
                }
            }
        } catch (Exception e) {
            Log.w(TAG, "getCurrentApp failed", e);
        }
        return "";
    }

    private AccessibilityNodeInfo findFocusNode() {
        AccessibilityNodeInfo root = getRootInActiveWindow();
        if (root == null) return null;

        AccessibilityNodeInfo focused = root.findFocus(AccessibilityNodeInfo.FOCUS_INPUT);
        root.recycle();
        return focused;
    }

    private List<AccessibilityNodeInfo> findNodesByViewId(String viewId) {
        AccessibilityNodeInfo root = getRootInActiveWindow();
        List<AccessibilityNodeInfo> result = new ArrayList<>();
        if (root != null) {
            result = root.findAccessibilityNodeInfosByViewId(viewId);
            root.recycle();
        }
        return result;
    }

    private List<AccessibilityNodeInfo> findNodesByText(String text) {
        AccessibilityNodeInfo root = getRootInActiveWindow();
        List<AccessibilityNodeInfo> result = new ArrayList<>();
        if (root != null) {
            result = root.findAccessibilityNodeInfosByText(text);
            root.recycle();
        }
        return result;
    }

    private AccessibilityNodeInfo findClickableAncestor(AccessibilityNodeInfo node) {
        AccessibilityNodeInfo parent = node.getParent();
        while (parent != null) {
            if (parent.isClickable()) return parent;
            AccessibilityNodeInfo grandParent = parent.getParent();
            if (grandParent == null) {
                parent.recycle();
                return null;
            }
            parent.recycle();
            parent = grandParent;
        }
        return null;
    }

    private JSONObject serializeNode(AccessibilityNodeInfo node) throws Exception {
        JSONObject obj = new JSONObject();
        obj.put("className", safeStr(node.getClassName()));
        obj.put("text", safeStr(node.getText()));
        obj.put("contentDesc", safeStr(node.getContentDescription()));
        obj.put("viewId", safeStr(node.getViewIdResourceName()));
        obj.put("packageName", safeStr(node.getPackageName()));
        obj.put("clickable", node.isClickable());
        obj.put("focusable", node.isFocusable());
        obj.put("editable", node.isEditable());
        obj.put("enabled", node.isEnabled());
        obj.put("checked", node.isChecked());
        obj.put("selected", node.isSelected());
        obj.put("scrollable", node.isScrollable());

        Rect bounds = new Rect();
        node.getBoundsInScreen(bounds);
        JSONObject boundsObj = new JSONObject();
        boundsObj.put("left", bounds.left);
        boundsObj.put("top", bounds.top);
        boundsObj.put("right", bounds.right);
        boundsObj.put("bottom", bounds.bottom);
        obj.put("bounds", boundsObj);

        int childCount = node.getChildCount();
        if (childCount > 0) {
            JSONArray children = new JSONArray();
            for (int i = 0; i < Math.min(childCount, 50); i++) {
                AccessibilityNodeInfo child = node.getChild(i);
                if (child != null) {
                    children.put(serializeNode(child));
                    child.recycle();
                }
            }
            obj.put("children", children);
        }

        return obj;
    }

    private String safeStr(CharSequence cs) {
        return cs != null ? cs.toString() : "";
    }

    private GestureDescription buildClick(int x, int y, long duration) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return null;
        Path path = new Path();
        path.moveTo(x, y);
        GestureDescription.StrokeDescription stroke = new GestureDescription.StrokeDescription(path, 0, duration);
        return new GestureDescription.Builder().addStroke(stroke).build();
    }

    private GestureDescription buildSwipe(int startX, int startY, int endX, int endY, long duration) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return null;
        Path path = new Path();
        path.moveTo(startX, startY);
        path.lineTo(endX, endY);
        GestureDescription.StrokeDescription stroke = new GestureDescription.StrokeDescription(path, 0, duration);
        return new GestureDescription.Builder().addStroke(stroke).build();
    }
}
