package com.ttstd.controlled.service;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.media.projection.MediaProjection;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ttstd.control.Action;
import com.ttstd.control.ControlMessage;
import com.ttstd.controlled.R;
import com.ttstd.controlled.accessibility.KeyboardAccessibilityService;
import com.ttstd.controlled.activity.connection.ConnectionRequestActivity;
import com.ttstd.controlled.activity.main.MainActivity;
import com.ttstd.controlled.input.AccessibilityInputUtils;
import com.ttstd.controlled.input.InputCommandHandler;
import com.ttstd.controlled.input.InputExecutor;
import com.ttstd.controlled.input.RootShellInputUtils;
import com.ttstd.controlled.input.ShellInputUtils;
import com.ttstd.controlled.input.SystemInputUtils;
import com.ttstd.controlled.signaling.SignalMessage;
import com.ttstd.controlled.signaling.WebSocketClient;
import com.ttstd.controlled.utils.AuthSettings;
import com.ttstd.controlled.utils.InputSettings;
import com.ttstd.controlled.utils.SignatureUtils;
import com.ttstd.controlled.webrtc.SelfCodecEncoder;
import com.ttstd.controlled.webrtc.WebRtcClient;

import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.ScreenCapturerAndroid;

import java.util.ArrayList;
import java.util.List;

public class ScreenCaptureService extends Service {

    private static final String TAG = "ScreenCaptureService";
    private static final String CHANNEL_ID = "screen_capture_channel";
    private static final int NOTIFICATION_ID = 1;

    public static final String EXTRA_RESULT_CODE = "result_code";
    public static final String EXTRA_RESULT_DATA = "result_data";
    public static final String EXTRA_SERVER_URL = "server_url";
    public static final String EXTRA_DEVICE_ID = "device_id";

    /**
     * 静态授权结果，解决部分 Android 10 设备跨进程/Intent 传递 Intent 时 Token 失效的问题
     */
    public static int sResultCode = Activity.RESULT_CANCELED;
    public static Intent sResultData = null;

    private ScreenCapturerAndroid screenCapturer;
    private WebSocketClient wsClient;
    private WebRtcClient webRtcClient;
    private InputCommandHandler inputHandler;
    private EglBase eglBase;
    private final Gson gson = new Gson();

    /**
     * 标记服务是否已经初始化，避免 onStartCommand 多次调用导致重复创建
     */
    private boolean isInitialized = false;

    /**
     * 静态实例引用，供弹窗 Activity 在无需绑定的情况下回调本服务。
     */
    private static ScreenCaptureService instance;

    public static ScreenCaptureService getInstance() {
        return instance;
    }

    /**
     * 本设备 ID（注册到信令服务器用），保存为字段以便弹窗回调使用。
     */
    private String deviceId;
    /**
     * 当前待处理的连接请求（OFFER）信息，供用户在弹窗中确认或拒绝。
     */
    private String pendingControllerId;
    private String pendingControllerName;
    private String pendingOfferSdp;
    /**
     * 当前正在控制本设备的控制端名称（用户名或设备 ID），用于断开提示。
     */
    private String controllingName;
    /**
     * 设备真实屏幕分辨率，用于分辨率切换时按宽高比计算采集尺寸。
     */
    private int realScreenWidth;
    private int realScreenHeight;
    /**
     * 当前实际采集分辨率与帧率（运行时可被本地/远端请求切换）。
     */
    private int currentCaptureWidth;
    private int currentCaptureHeight;
    private int currentCaptureFps;
    /** 屏幕最大刷新率（远程/本地请求的帧率不会超过该值）。 */
    private int maxCaptureFps = 60;
    /** 按屏幕刷新率筛选出的适合帧率档位（升序）。 */
    private List<Integer> supportedFpsList = new ArrayList<>();
    /**
     * 主线程 Handler，用于从 WebRTC 回调线程切回主线程执行 UI 提示。
     */
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    /**
     * 标记服务是否正在关闭，避免重复停止/重复弹窗。
     */
    private boolean isShuttingDown = false;

    // ---- 屏幕串流"自编码"模式 ----
    private int resultCode;                 // 屏幕捕获授权结果码（供 WebRTC 采集使用）
    private Intent resultDataIntent;       // 屏幕捕获授权 Intent（同上）
    private int currentStreamMode = SelfCodecEncoder.STREAM_MODE_WEBRTC;
    private SelfCodecEncoder selfEncoder;     // 自编码 H264 编码器（复用 WebRTC 采集帧，不单独申请虚拟显示）

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        createNotificationChannel();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            stopSelf();
            return START_NOT_STICKY;
        }

        // 创建前台通知
        Notification notification = createNotification();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
        } else {
            startForeground(NOTIFICATION_ID, notification);
        }

        // 优先从静态变量获取授权结果（解决 Android 10+ Intent 传递 Token 失效），若无则 fallback 到 Intent extras。
        int resultCode = (sResultCode != Activity.RESULT_CANCELED)
                ? sResultCode : intent.getIntExtra(EXTRA_RESULT_CODE, Activity.RESULT_CANCELED);
        Intent resultData = (sResultData != null)
                ? sResultData : intent.getParcelableExtra(EXTRA_RESULT_DATA);

        String serverUrl = intent.getStringExtra(EXTRA_SERVER_URL);
        deviceId = intent.getStringExtra(EXTRA_DEVICE_ID);

        // 保存授权结果，供后续切换模式（如自编码模式）重新申请 MediaProjection 时使用。
        this.resultCode = resultCode;
        this.resultDataIntent = resultData;

        if (resultCode != Activity.RESULT_OK || resultData == null || serverUrl == null || deviceId == null) {
            Log.e(TAG, "Invalid service parameters: resultCode=" + resultCode + ", hasData=" + (resultData != null));
            stopSelf();
            return START_NOT_STICKY;
        }

        if (isInitialized) {
            Log.i(TAG, "Service already initialized, ignoring parameters");
            return START_NOT_STICKY;
        }
        isInitialized = true;

        // ... 获取分辨率逻辑保持不变 ...
        // 获取屏幕真实尺寸和刷新率
        WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            wm.getDefaultDisplay().getRealMetrics(dm);
        } else {
            wm.getDefaultDisplay().getMetrics(dm);
        }
        int realWidth = dm.widthPixels;
        int realHeight = dm.heightPixels;
        this.realScreenWidth = realWidth;
        this.realScreenHeight = realHeight;

        // 分辨率缩放逻辑：如果设备分辨率大于 1080p，则等比例缩小到 1080p 以内
        int captureWidth = realWidth;
        int captureHeight = realHeight;
        // 模拟器或低配设备建议使用 720p 甚至 480p，否则软件编码 1080p 会导致卡死
        int maxResolution = 1920; // 1080p 的长边

        if (Math.max(realWidth, realHeight) > maxResolution) {
            float scale = (float) maxResolution / Math.max(realWidth, realHeight);
            captureWidth = Math.round(realWidth * scale);
            captureHeight = Math.round(realHeight * scale);
            // 确保是偶数，部分编码器要求宽高为偶数
            if (captureWidth % 2 != 0) captureWidth--;
            if (captureHeight % 2 != 0) captureHeight--;
        }

        float refreshRate = 30;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            try {
                refreshRate = wm.getDefaultDisplay().getRefreshRate();
            } catch (Exception ignored) {
            }
        }
        int maxFps = Math.round(refreshRate);
        if (maxFps < 30) maxFps = 30;
        this.maxCaptureFps = maxFps;
        // 根据屏幕刷新率生成适合的帧率档位，供本地 UI 与控制端选择。
        this.supportedFpsList = buildSupportedFpsList(maxFps);

        // 初始默认 30fps：屏幕控制 30fps 已足够跟手，并显著降低 H264 硬编延迟（影响“跟手”体验）。
        // 更高的帧率会加重编码缓冲与带宽压力，用户可按需通过帧率档位手动调高。
        int fps = Math.min(30, maxFps);

        Log.i(TAG, "Real resolution: " + realWidth + "x" + realHeight);
        Log.i(TAG, "Capture resolution: " + captureWidth + "x" + captureHeight + " @ " + fps + "fps");

        // 记录初始采集分辨率，供 UI 与运行时切换使用
        this.currentCaptureWidth = captureWidth;
        this.currentCaptureHeight = captureHeight;
        this.currentCaptureFps = fps;

        // 输入处理器必须使用真实分辨率进行坐标映射
        inputHandler = new InputCommandHandler(realWidth, realHeight, createInputExecutor());
        // 控制端发来的 SET_RESOLUTION 指令转交本服务处理（坐标映射与输入执行器无关）。
        inputHandler.setResolutionRequestListener(this::onRemoteResolutionRequested);
        inputHandler.setStreamModeListener(this::onStreamModeRequested);

        // 启动心脏起搏器，解决画面静止或模拟器不推帧的问题
        mainHandler.removeCallbacks(heartbeatRunnable);
        mainHandler.post(heartbeatRunnable);

        // Android 10+ 强烈建议在 startForeground 之后延时一点点再初始化 MediaProjection，
        // 确保系统已经感知到服务已切换为 FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION。
        final int finalCaptureWidth = captureWidth;
        final int finalCaptureHeight = captureHeight;
        final int finalFps = fps;
        mainHandler.postDelayed(() -> {
            if (!isShuttingDown) {
                startScreenCapture(resultCode, resultData, serverUrl, deviceId, finalCaptureWidth, finalCaptureHeight, finalFps);
            }
        }, 200);

        return START_NOT_STICKY;
    }

    /**
     * 根据当前应用权限/环境选择最合适的输入执行器：
     * 1. 系统签名应用优先使用隐藏 API 注入（支持精确触摸与任意按键）；
     * 2. 已开启无障碍服务时使用 AccessibilityInputUtils（无需 root/系统签名）；
     * 3. 兜底使用 root shell 方式。
     */
    private InputExecutor createInputExecutor() {
        String method = InputSettings.getInputMethod(this);
        switch (method) {
            case InputSettings.METHOD_SYSTEM:
                Log.i(TAG, "Input executor: SystemInputUtils (user selected)");
                return new SystemInputUtils();
            case InputSettings.METHOD_ROOT:
                Log.i(TAG, "Input executor: RootShellInputUtils (user selected)");
                return new RootShellInputUtils();
            case InputSettings.METHOD_ACCESSIBILITY:
                Log.i(TAG, "Input executor: AccessibilityInputUtils (user selected)");
                return new AccessibilityInputUtils();
            case InputSettings.METHOD_SHELL:
                Log.i(TAG, "Input executor: ShellInputUtils (user selected)");
                return new ShellInputUtils();
            case InputSettings.METHOD_AUTO:
            default:
                return autoSelectInputExecutor();
        }
    }

    /**
     * 根据当前应用权限/环境自动选择最合适的输入执行器：
     * 1. 系统签名应用优先使用隐藏 API 注入（支持精确触摸与任意按键）；
     * 2. 已获取 root 时使用 root shell；
     * 3. 已开启无障碍服务时使用 AccessibilityInputUtils（无需 root/系统签名）；
     * 4. 兜底使用普通 shell 方式。
     */
    private InputExecutor autoSelectInputExecutor() {
        if (SignatureUtils.isSystemSignature(this) || SignatureUtils.isSharedSystemUid(this)) {
            Log.i(TAG, "Input executor: SystemInputUtils (system signature)");
            return new SystemInputUtils();
        }
        if (SignatureUtils.isDeviceRooted() || SignatureUtils.isAppHasRootPermission()) {
            Log.i(TAG, "Input executor: RootShellInputUtils (rooted)");
            return new RootShellInputUtils();
        }
        if (KeyboardAccessibilityService.getInstance() != null) {
            Log.i(TAG, "Input executor: AccessibilityInputUtils (accessibility enabled)");
            return new AccessibilityInputUtils();
        }
        Log.i(TAG, "Input executor: ShellInputUtils (fallback)");
        return new ShellInputUtils();
    }

    private final IBinder binder = new LocalBinder();

    public class LocalBinder extends Binder {
        public ScreenCaptureService getService() {
            return ScreenCaptureService.this;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public WebRtcClient getWebRtcClient() {
        return webRtcClient;
    }

    /**
     * 供 MainActivity 启动/恢复时查询：服务是否正在采集并串流屏幕。
     * 用于 Activity 被销毁后重新打开时恢复界面状态，避免“服务仍在后台串流但界面显示已停止”的状态丢失问题。
     */
    public boolean isStreaming() {
        return screenCapturer != null && !isShuttingDown;
    }

    /**
     * 切换屏幕采集分辨率（本地 UI 调用）。
     *
     * @param longEdge 目标长边像素；<=0 表示使用设备原生分辨率（按屏幕宽高比缩放）。
     */
    public void changeResolutionByLongEdge(int longEdge) {
        requestResolutionChange(longEdge, 0, 0, false);
    }

    /**
     * 仅切换采集帧率（保持当前分辨率），供本地 UI 使用。
     */
    public void changeFps(int fps) {
        requestResolutionChange(currentCaptureWidth, currentCaptureHeight, fps, false);
    }

    /**
     * 返回按屏幕刷新率筛选出的适合帧率档位（升序）。
     */
    public List<Integer> getSupportedFpsList() {
        return supportedFpsList;
    }

    /**
     * 根据屏幕最大刷新率生成帧率档位列表：从常用档位中筛选不超过刷新率的值，
     * 若刷新率本身不在常用档位中则追加（如 75/144Hz 屏幕）。
     */
    private static List<Integer> buildSupportedFpsList(int maxFps) {
        List<Integer> list = new ArrayList<>();
        int[] candidates = {15, 24, 30, 60, 90, 120};
        for (int c : candidates) {
            if (c <= maxFps) list.add(c);
        }
        if (list.isEmpty() || list.get(list.size() - 1) < maxFps) {
            list.add(maxFps);
        }
        return list;
    }

    /**
     * 返回当前采集分辨率（宽/高/帧率）。
     */
    public int[] getCurrentCaptureResolution() {
        return new int[]{currentCaptureWidth, currentCaptureHeight, currentCaptureFps};
    }

    /**
     * 返回设备原生分辨率的长边（像素），供 UI 生成“原始画质”选项。
     */
    public int getNativeLongEdge() {
        return Math.max(realScreenWidth, realScreenHeight);
    }

    /**
     * 控制端经 DataChannel 发来的 SET_RESOLUTION 指令入口。
     */
    private void onRemoteResolutionRequested(int width, int height, int fps) {
        requestResolutionChange(width, height, fps, true);
    }

    /**
     * 解析并应用目标采集分辨率。
     * 语义（与控制端约定一致）：
     * - width <= 0：使用设备原生分辨率。
     * - height <= 0：仅按 width 指定长边，按屏幕宽高比计算另一维度。
     * - 否则使用精确 width x height。
     * 最终宽高会偶数对齐（部分 H264 编码器要求），并触发一次关键帧刷新远端画面。
     *
     * @param width      目标宽度/长边（像素）
     * @param height     目标高度（像素，<=0 时按宽高比计算）
     * @param fps        目标帧率（<=0 时沿用当前帧率）
     * @param fromRemote 是否由远端控制端触发（仅用于 UI 提示区分）
     */
    public void requestResolutionChange(int width, int height, int fps, boolean fromRemote) {
        int[] size = resolveCaptureSize(width, height);
        int targetW = size[0];
        int targetH = size[1];
        int targetFps = fps > 0 ? fps : currentCaptureFps;
        if (targetFps <= 0) targetFps = 30;
        // 帧率不超过屏幕刷新率（超过无意义，只会浪费编码/带宽资源）。
        if (maxCaptureFps > 0 && targetFps > maxCaptureFps) targetFps = maxCaptureFps;

        if (webRtcClient == null) {
            Log.w(TAG, "requestResolutionChange ignored: WebRTC not ready");
            return;
        }

        webRtcClient.changeCaptureFormat(targetW, targetH, targetFps);
        currentCaptureWidth = targetW;
        currentCaptureHeight = targetH;
        currentCaptureFps = targetFps;

        // 关键：分辨率切换后，如果是自编码模式，同样需要触发扰动确保产生首帧，避免静态画面黑屏。
        if (currentStreamMode == SelfCodecEncoder.STREAM_MODE_SELF_CODEC) {
            updateNotification("正在切换分辨率...");
            // 自编码器（MediaCodec）不支持动态切换分辨率，必须重启编码器实例。
            stopSelfCodecEncoder();
            startSelfCodecEncoder();
        }

        Log.i(TAG, "Capture resolution changed -> " + targetW + "x" + targetH + " @ " + targetFps + "fps (remote=" + fromRemote + ")");
        showToast(getString(R.string.resolution_changed_message, targetW, targetH));

        if (resolutionListener != null) {
            resolutionListener.onResolutionChanged(targetW, targetH, targetFps, fromRemote);
        }
    }

    /**
     * 根据指令参数解析出最终采集尺寸（保留屏幕宽高比、偶数对齐）。
     */
    private int[] resolveCaptureSize(int width, int height) {
        int w, h;
        if (width <= 0) {
            w = realScreenWidth;
            h = realScreenHeight;
        } else if (height <= 0) {
            int longEdge = Math.max(realScreenWidth, realScreenHeight);
            float scale = (float) width / longEdge;
            w = Math.round(realScreenWidth * scale);
            h = Math.round(realScreenHeight * scale);
        } else {
            w = width;
            h = height;
        }
        // 偶数对齐（部分 H264 编码器要求宽高为偶数）
        if (w % 2 != 0) w--;
        if (h % 2 != 0) h--;
        if (w <= 0) w = 2;
        if (h <= 0) h = 2;
        return new int[]{w, h};
    }

    // ---- 屏幕串流"自编码"模式 ----

    /**
     * 控制端经 SET_STREAM_MODE 请求切换串流模式。
     */
    private void onStreamModeRequested(int mode) {
        setStreamMode(mode);
    }

    /**
     * 切换屏幕串流模式：
     * - {@link SelfCodecEncoder#STREAM_MODE_WEBRTC}：使用 WebRTC 默认媒体轨道（ScreenCapturerAndroid）。
     * - {@link SelfCodecEncoder#STREAM_MODE_SELF_CODEC}：暂停 WebRTC 采集，改用自建 MediaCodec 编码，
     * 经 DataChannel 的 video_channel 透传裸 H.264 码流，由控制端自建解码器渲染。
     */
    public void setStreamMode(int mode) {
        if (mode == currentStreamMode) {
            return;
        }
        if (mode == SelfCodecEncoder.STREAM_MODE_SELF_CODEC) {
            // 切到自编码：WebRTC 的屏幕采集保持运行（共享同一条采集管线），
            // 自编码编码器只是额外"旁路"这些帧，不再单独申请 VirtualDisplay，
            // 因此无需 stop/startCapture，规避 Android 14 单次投屏授权令牌的限制。
            if (!startSelfCodecEncoder()) {
                // 启动失败：保持 WebRTC 模式并如实上报，避免黑屏。
                Log.e(TAG, "switch to self codec failed, stay webrtc mode");
                currentStreamMode = SelfCodecEncoder.STREAM_MODE_WEBRTC;
                reportStreamMode(SelfCodecEncoder.STREAM_MODE_WEBRTC);
                return;
            }
            updateNotification("正在使用自编码模式串流...");
        } else {
            // 切回 WebRTC：停止自编码旁路，WebRTC 采集与媒体流本就一直在线。
            stopSelfCodecEncoder();
            updateNotification("正在使用 WebRTC 模式串流...");
        }
        currentStreamMode = mode;
        reportStreamMode(mode);
    }

    /**
     * 返回当前串流模式。
     */
    public int getCurrentStreamMode() {
        return currentStreamMode;
    }

    /**
     * 启动自编码通道。返回 false 表示启动失败（调用方应回退 WebRTC 模式）。
     */
    private boolean startSelfCodecEncoder() {
        if (selfEncoder != null) {
            Log.w(TAG, "self codec encoder already running");
            return true;
        }
        if (webRtcClient == null || !webRtcClient.isVideoDataChannelOpen()) {
            Log.w(TAG, "startSelfCodecEncoder: video channel not ready yet");
        }
        int[] res = getCurrentCaptureResolution();
        // 注意：自编码编码器不再创建自己的 MediaProjection/VirtualDisplay，
        // 而是复用 WebRTC 的屏幕采集帧（通过 WebRtcClient.addSink 转发），
        // 从根本上消除 Android 14 上"无法复用投屏令牌 / 无法创建第二个虚拟显示"的崩溃。
        selfEncoder = new SelfCodecEncoder(
                res[0], res[1], res[2],
                webRtcClient != null ? webRtcClient.getVideoDataChannel() : null,
                new SelfCodecEncoder.Callback() {
                    @Override
                    public void onEncoderStarted(int w, int h, int f) {
                        Log.i(TAG, "self codec encoder started: " + w + "x" + h);
                    }

                    @Override
                    public void onEncoderError(String error) {
                        Log.e(TAG, "self codec encoder error: " + error);
                        // 出错时回退到 WebRTC 模式
                        mainHandler.post(() -> setStreamMode(SelfCodecEncoder.STREAM_MODE_WEBRTC));
                    }
                });
        selfEncoder.start();
        // start() 内部捕获异常后 running 仍为 false，需据此判断真实启动结果，
        // 否则会误报 SELF_CODEC 模式已生效而实际无任何画面（黑屏）。
        if (!selfEncoder.isRunning()) {
            Log.e(TAG, "self codec encoder failed to start, cleaning up");
            stopSelfCodecEncoder();
            return false;
        }
        // 立即请求一个关键帧（IDR），让控制端尽快拿到 SPS/PPS 并完成解码器配置。
        selfEncoder.requestKeyFrame();
        // 把 WebRTC 采集到的每一帧旁路给自编码编码器
        if (webRtcClient != null) {
            webRtcClient.setSelfCodecFrameListener(frame -> selfEncoder.feedFrame(frame));
        }
        return true;
    }

    private void stopSelfCodecEncoder() {
        if (webRtcClient != null) {
            webRtcClient.setSelfCodecFrameListener(null);
        }
        if (selfEncoder != null) {
            selfEncoder.release();
            selfEncoder = null;
        }
    }

    /**
     * 经 control DataChannel 向控制端回报当前生效的串流模式。
     */
    private void reportStreamMode(int mode) {
        if (webRtcClient == null) return;
        try {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.REPORT_STREAM_MODE)
                    .setStreamMode(mode)
                    .build();
            webRtcClient.sendControlCommand(msg);
        } catch (Exception e) {
            Log.e(TAG, "reportStreamMode failed", e);
        }
    }

    /**
     * 状态回调。由已 bind 的 MainActivity 注册，用于把连接状态同步到界面。
     */
    public interface ServiceStateListener {
        /**
         * 信令服务器已连接（首次连接或自动重连成功）。
         */
        void onSignalConnected();

        /**
         * 信令服务器断开（WebSocket 断开，客户端会自动重连）。
         */
        void onSignalDisconnected();

        /**
         * 远程控制端断开，参数为控制端名称（用户名或设备 ID）。
         */
        void onControllerDisconnected(String name);
    }

    private ServiceStateListener stateListener;

    public void setStateListener(ServiceStateListener listener) {
        this.stateListener = listener;
    }

    /**
     * 分辨率变化回调。由已 bind 的 MainActivity 注册，用于在界面上同步当前采集分辨率。
     */
    public interface ResolutionChangeListener {
        void onResolutionChanged(int width, int height, int fps, boolean fromRemote);
    }

    private ResolutionChangeListener resolutionListener;

    public void setResolutionListener(ResolutionChangeListener listener) {
        this.resolutionListener = listener;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isShuttingDown = true;
        sResultCode = Activity.RESULT_CANCELED;
        sResultData = null;
        if (instance == this) {
            instance = null;
        }
        if (screenCapturer != null) {
            screenCapturer.stopCapture();
            screenCapturer.dispose();
        }
        if (selfEncoder != null) {
            selfEncoder.release();
            selfEncoder = null;
        }
        if (webRtcClient != null) {
            webRtcClient.close();
        }
        if (wsClient != null) {
            wsClient.disconnect();
        }
        if (eglBase != null) {
            eglBase.release();
            eglBase = null;
        }
        if (inputHandler != null) {
            inputHandler.release();
            inputHandler = null;
        }
    }

    private void startScreenCapture(int resultCode, Intent resultData, String serverUrl, String deviceId, int width, int height, int fps) {
        // 初始化 EGL
        eglBase = EglBase.create();

        // 初始化 WebSocket
        wsClient = new WebSocketClient(serverUrl, deviceId);
        wsClient.setListener(new WebSocketClient.SignalListener() {
            @Override
            public void onConnected() {
                Log.i(TAG, "Connected to signal server");
                isShuttingDown = false; // 重连成功后重新武装
                updateNotification("远程控制服务正在运行");
                if (stateListener != null) {
                    stateListener.onSignalConnected();
                }
            }

            @Override
            public void onDisconnected() {
                Log.i(TAG, "Disconnected from signal server");
                mainHandler.post(() -> onSignalServerDisconnected());
            }

            @Override
            public void onError(String error) {
                Log.e(TAG, "WebSocket onError: " + error);
                mainHandler.post(() -> onSignalServerDisconnected());
            }

            @Override
            public void onMessage(SignalMessage message) {
                handleSignalMessage(message);
            }
        });
        wsClient.connect();

        // 初始化 WebRTC
        webRtcClient = new WebRtcClient(this, wsClient, deviceId);
        webRtcClient.initialize(eglBase);
        // 供 REPORT_RESOLUTION 上报给控制端，控制端据此展示帧率档位。
        webRtcClient.setSupportedFpsList(supportedFpsList);
        webRtcClient.setInputCallback(commandBytes -> inputHandler.handleCommand(commandBytes));
        webRtcClient.setIceEventListener(() -> {
            mainHandler.post(() -> {
                if (currentStreamMode == SelfCodecEncoder.STREAM_MODE_SELF_CODEC && selfEncoder != null) {
                    selfEncoder.requestKeyFrame();
                    updateNotification("远程控制已连接 (自编码)");
                } else {
                    updateNotification("远程控制已连接");
                }
            });
        });
        // 注册远程控制断开回调：控制端断线时提示其用户名。
        webRtcClient.setRemoteDisconnectCallback(controllerId ->
                mainHandler.post(() -> onControllerDisconnected(controllerId)));

        // 初始化屏幕捕获
        screenCapturer = new ScreenCapturerAndroid(resultData, new MediaProjection.Callback() {
            @Override
            public void onStop() {
                super.onStop();
                Log.e(TAG, "MediaProjection stopped (ScreenCapturerAndroid)");
            }
        });

        webRtcClient.setVideoCapturer(screenCapturer, width, height, fps);
    }

    private void handleSignalMessage(SignalMessage message) {
        String type = message.getType();
        if (type == null) return;

        switch (type.toUpperCase()) {
            case "OFFER":
                handleOffer(message);
                break;
            case "ICE_CANDIDATE":
                handleIceCandidate(message);
                break;
        }
    }

    private void handleOffer(SignalMessage message) {
        // 已有待确认请求时不重复弹窗，避免覆盖当前对话框的状态。
        if (pendingControllerId != null) {
            Log.i(TAG, "Ignore OFFER from " + message.getFromDeviceId() + ": a request is already pending");
            return;
        }

        String offerSdp = null;
        String controllerName = null;
        try {
            JsonObject payload = gson.fromJson(message.getPayload(), JsonObject.class);
            offerSdp = payload.get("sdp").getAsString();
            // 控制端若在 OFFER 中携带用户名则使用，否则回退到设备 ID。
            if (payload.has("username") && !payload.get("username").isJsonNull()) {
                controllerName = payload.get("username").getAsString();
            }
        } catch (Exception e) {
            Log.e(TAG, "Error parsing offer SDP", e);
            return;
        }

        String controllerId = message.getFromDeviceId();
        // 暂存请求信息
        pendingControllerId = controllerId;
        pendingControllerName = (controllerName != null && !controllerName.isEmpty())
                ? controllerName : controllerId;
        pendingOfferSdp = offerSdp;

        // 鉴权模式：免密(NONE) / 动态验证码(CODE) / 固定密码(PASSWORD)。
        // 服务器仅做转发，真实校验在此完成（密钥不离开被控端）。
        String rawAuthType = message.getAuthType();
        String authType = (rawAuthType == null || rawAuthType.trim().isEmpty())
                ? "NONE" : rawAuthType.trim();
        boolean isNoAuth = "NONE".equalsIgnoreCase(authType);
        if (isNoAuth) {
            // 免密连接：若被控端已关闭免密，则直接拒绝，要求使用验证码/密码
            if (!AuthSettings.isNoAuthAllowed(this)) {
                Log.w(TAG, "Reject 免密连接 from " + controllerId + ": 被控端已关闭免密连接");
                sendAuthRejected("被控端已关闭免密连接，请使用动态验证码或固定密码");
                return;
            }
            // 免密连接：弹出手动确认弹窗，由用户决定是否接受
            Log.i(TAG, "免密连接，转交用户手动确认: " + controllerId);
            Intent dialogIntent = new Intent(this, ConnectionRequestActivity.class);
            dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            dialogIntent.putExtra(ConnectionRequestActivity.EXTRA_CONTROLLER_ID, controllerId);
            dialogIntent.putExtra(ConnectionRequestActivity.EXTRA_AUTH_TYPE, "NONE");
            startActivity(dialogIntent);
            return;
        }

        // 携带鉴权信息：被控端本地校验，通过则自动接受
        String authValue = message.getAuthValue();
        String failReason = verifyAuth(authType, authValue);
        if (failReason == null) {
            // 校验通过：自动接受，直接建立连接（无需手动确认）
            Log.i(TAG, "Auth passed (" + authType + "), auto-accepting " + controllerId);
            mainHandler.post(this::acceptConnection);
        } else {
            Log.w(TAG, "Auth failed (" + authType + "): " + failReason + " from " + controllerId);
            sendAuthRejected(failReason);
        }
    }

    /**
     * 校验控制端发来的鉴权信息。返回 null 表示通过，否则返回失败原因。
     * CODE:     与被控端当前动态验证码比对；
     * PASSWORD: 与被控端固定密码比对。
     */
    private String verifyAuth(String authType, String authValue) {
        String valueToVerify = (authValue == null) ? "" : authValue;
        if ("CODE".equalsIgnoreCase(authType)) {
            String expected = AuthSettings.getDynamicCode(this);
            if (expected == null || expected.isEmpty()) {
                return "被控端未生成动态验证码";
            }
            return expected.equals(valueToVerify) ? null : "动态验证码错误";
        } else if ("PASSWORD".equalsIgnoreCase(authType)) {
            String expected = AuthSettings.getFixedPassword(this);
            if (expected == null || expected.isEmpty()) {
                return "被控端未设置固定密码";
            }
            return expected.equals(valueToVerify) ? null : "固定密码错误";
        }
        return "未知的鉴权类型: " + authType;
    }

    /**
     * 鉴权失败时，向控制端回送带原因的拒绝消息并清理待处理状态。
     */
    private void sendAuthRejected(String reason) {
        if (pendingControllerId == null || wsClient == null) {
            return;
        }
        String controllerId = pendingControllerId;
        clearPendingRequest();
        SignalMessage msg = new SignalMessage();
        msg.setType("CONNECTION_REJECTED");
        msg.setFromDeviceId(deviceId);
        msg.setToDeviceId(controllerId);
        msg.setDeviceType("CONTROLLED");
        JsonObject payload = new JsonObject();
        payload.addProperty("reason", reason);
        msg.setPayload(payload.toString());
        wsClient.sendMessage(msg);
        Log.i(TAG, "Sent CONNECTION_REJECTED(" + reason + ") to " + controllerId);
    }

    /**
     * 用户在弹窗中点击“接受”：创建 Answer 完成连接。
     */
    public void acceptConnection() {
        if (pendingOfferSdp != null && pendingControllerId != null && webRtcClient != null) {
            String offerSdp = pendingOfferSdp;
            String controllerId = pendingControllerId;
            controllingName = pendingControllerName;
            clearPendingRequest();

            // 1. 通过更新通知文字触发屏幕刷新（扰动方案）
            // 状态栏的变化会强制 MediaProjection 产生至少一帧，解决静态画面黑屏问题。
            updateNotification("正在接受 " + controllingName + " 的控制...");

            // 2. 建立连接
            webRtcClient.createPeerConnectionAndAnswer(offerSdp, controllerId);
        }
    }

    private void updateNotification(String contentText) {
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(NOTIFICATION_ID, createNotification(contentText));
    }

    /**
     * 用户在弹窗中点击“拒绝”：向主控端回送拒绝通知，不建立连接。
     */
    public void rejectConnection() {
        if (pendingControllerId != null && wsClient != null) {
            String controllerId = pendingControllerId;
            clearPendingRequest();
            sendConnectionRejected(controllerId);
        }
    }

    private void clearPendingRequest() {
        pendingControllerId = null;
        pendingControllerName = null;
        pendingOfferSdp = null;
    }

    /**
     * 信令服务器断开：通过已绑定的 MainActivity 同步状态并停止屏幕共享，
     * 由 MainActivity 负责解绑/停止服务并刷新界面，避免直接在服务内
     * stopSelf 而在 bound 状态下无法真正销毁的问题。
     */
    private void onSignalServerDisconnected() {
        if (isShuttingDown) return;
        Log.i(TAG, "Signal server disconnected, will auto-reconnect");
        // 不再直接停止服务：WebSocketClient 会自动重连，服务保持运行。
        showToast(getString(R.string.signal_reconnecting_message));
        updateNotification("信令断开，正在重连...");
        if (stateListener != null) {
            stateListener.onSignalDisconnected();
        }
    }

    /**
     * 远程控制端断开：提示当前控制端（用户名/设备 ID）已断开连接。
     * 仅提示并同步状态，不停止服务，以便被控端可继续接收新的连接请求。
     */
    private void onControllerDisconnected(String controllerId) {
        if (isShuttingDown) return;
        String name = (controllingName != null && !controllingName.isEmpty())
                ? controllingName : controllerId;
        Log.i(TAG, "Remote controller disconnected: " + name);
        showToast(getString(R.string.controller_disconnected_message, name));

        // 断开后恢复原始通知状态
        updateNotification("远程控制服务正在运行");

        if (stateListener != null) {
            stateListener.onControllerDisconnected(name);
        }
        // 断开后清空控制端身份，允许后续重新接受新的连接。
        controllingName = null;
    }

    private void showToast(String text) {
        try {
            Toast.makeText(this, text, Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Log.w(TAG, "Failed to show toast: " + e.getMessage());
        }
    }

    private void sendConnectionRejected(String controllerId) {
        SignalMessage msg = new SignalMessage();
        msg.setType("CONNECTION_REJECTED");
        msg.setFromDeviceId(deviceId);
        msg.setToDeviceId(controllerId);
        msg.setDeviceType("CONTROLLED");

        JsonObject payload = new JsonObject();
        payload.addProperty("reason", "被控端拒绝了远程连接请求");
        msg.setPayload(payload.toString());

        wsClient.sendMessage(msg);
        Log.i(TAG, "Sent CONNECTION_REJECTED to " + controllerId);
    }

    private final Runnable heartbeatRunnable = new Runnable() {
        @Override
        public void run() {
            if (!isShuttingDown) {
                updateNotificationHeartbeat();
                mainHandler.postDelayed(this, 1000);
            }
        }
    };

    private int heartbeatCount = 0;

    private void updateNotificationHeartbeat() {
        String dots = "";
        for (int i = 0; i < (heartbeatCount % 3) + 1; i++) dots += ".";
        heartbeatCount++;
        updateNotification("远程控制服务运行中" + dots);
    }

    private void handleIceCandidate(SignalMessage message) {
        try {
            JsonObject payload = gson.fromJson(message.getPayload(), JsonObject.class);
            String sdpMid = payload.get("sdpMid").getAsString();
            int sdpMLineIndex = payload.get("sdpMLineIndex").getAsInt();
            String candidate = payload.get("candidate").getAsString();

            IceCandidate iceCandidate = new IceCandidate(sdpMid, sdpMLineIndex, candidate);
            webRtcClient.addIceCandidate(iceCandidate);
        } catch (Exception e) {
            Log.e(TAG, "Error parsing ICE candidate", e);
        }
    }

    private Notification createNotification() {
        return createNotification("远程控制服务正在运行");
    }

    private Notification createNotification(String contentText) {
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("屏幕共享中")
                .setContentText(contentText)
                .setSmallIcon(android.R.drawable.ic_menu_camera)
                .setContentIntent(pendingIntent)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setOngoing(true)
                .build();
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "屏幕共享服务",
                    NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription("远程控制屏幕采集前台服务");
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }
}
