package com.ttstd.controller.webrtc;

import android.content.Context;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ttstd.control.Action;
import com.ttstd.controller.signaling.SignalMessage;
import com.ttstd.controller.signaling.WebSocketClient;
import com.ttstd.control.ControlMessage;

import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.MediaStreamTrack;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.RtpTransceiver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoSink;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.VideoTrack;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class WebRtcClient {

    private static final String TAG = "WebRtcClient";
    public static final String DATA_CHANNEL_LABEL = "control_channel";
    public static final String VIDEO_CHANNEL_LABEL = "video_channel"; // 自编码视频透传通道
    public static final int STREAM_MODE_WEBRTC = 0;     // WebRTC 内置媒体流
    public static final int STREAM_MODE_SELF_CODEC = 1;  // 自编码（MediaCodec + DataChannel）

    private final Context context;
    private final WebSocketClient wsClient;
    private final String deviceId;
    private final Gson gson = new Gson();
    private String pendingAuthType;
    private String pendingAuthValue;

    private PeerConnectionFactory peerConnectionFactory;
    private PeerConnection peerConnection;
    private DataChannel dataChannel;
    private DataChannel videoDataChannel; // 自编码视频透传通道（本端创建）
    private SelfCodecDecoder selfCodecDecoder;
    private StreamModeReportListener streamModeReportListener;
    private EglBase eglBase;
    private String currentControlledId;
    private SurfaceViewRenderer remoteSurfaceView;
    private VideoTrack remoteVideoTrack;
    private VideoSink recorderSink; // 视频录制器（可作为额外的 sink 接入远端视频轨道）
    private boolean recording = false;

    public interface ConnectionListener {
        void onConnectionEstablished();
        void onConnectionFailed(String error);
        void onDisconnected();
    }

    public interface StreamModeReportListener {
        void onStreamModeReported(int mode);
    }

    /** 被控端上报当前采集分辨率/帧率与支持的帧率档位（REPORT_RESOLUTION）。 */
    public interface ResolutionReportListener {
        void onResolutionReported(int width, int height, int fps, List<Integer> supportedFps);
    }

    private ResolutionReportListener resolutionReportListener;

    public void setResolutionReportListener(ResolutionReportListener l) {
        this.resolutionReportListener = l;
    }

    private ConnectionListener connectionListener;

    public WebRtcClient(Context context, WebSocketClient wsClient, String deviceId) {
        this.context = context;
        this.wsClient = wsClient;
        this.deviceId = deviceId;
    }

    public void setConnectionListener(ConnectionListener listener) {
        this.connectionListener = listener;
    }

    public void initialize(EglBase eglBase) {
        this.eglBase = eglBase;
        PeerConnectionFactory.InitializationOptions initOptions =
                PeerConnectionFactory.InitializationOptions.builder(context)
                        .createInitializationOptions();
        PeerConnectionFactory.initialize(initOptions);

        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        
        // 创建视频编解码工厂，以支持硬件加速和各种格式
        VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(
                eglBase.getEglBaseContext(), true, true);
        VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext());

        peerConnectionFactory = PeerConnectionFactory.builder()
                .setOptions(options)
                .setVideoEncoderFactory(encoderFactory)
                .setVideoDecoderFactory(decoderFactory)
                .createPeerConnectionFactory();
    }

    public void createOffer(String controlledId, SurfaceViewRenderer remoteRenderer,
                           String authType, String authValue) {
        this.currentControlledId = controlledId;
        this.pendingAuthType = authType;
        this.pendingAuthValue = authValue;

        List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        // 添加 STUN 和 TURN 服务器
        // 注意：如果是 TURN 服务器，必须使用 turn: 前缀
        iceServers.add(PeerConnection.IceServer.builder("turn:175.178.213.60:3478").setUsername("fanhuitong").setPassword("Fan19961207..").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:175.178.213.60:3478").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("turn:47.242.112.133:3478").setUsername("ttstd").setPassword("fanhuitong").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:47.242.112.133:3478").createIceServer());
        // iceServers.add(PeerConnection.IceServer.builder("turn:192.168.5.224:3478").setUsername("tt").setPassword("fht").createIceServer());
        // iceServers.add(PeerConnection.IceServer.builder("stun:192.168.5.224:3478").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:stun1.l.google.com:19302").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("stun:stun2.l.google.com:19302").createIceServer());
        PeerConnection.RTCConfiguration config = new PeerConnection.RTCConfiguration(iceServers);
        config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
        config.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        config.iceCandidatePoolSize = 10;

        // 建议添加以下配置，增强复杂网络下的稳定性
        config.iceTransportsType = PeerConnection.IceTransportsType.ALL;
        config.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED;

        PeerObserver peerObserver = new PeerObserver(new PeerObserver.PeerEventListener() {
            @Override
            public void onIceCandidate(IceCandidate candidate) {
                sendIceCandidate(candidate, controlledId);
            }

            @Override
            public void onIceConnectionChange(PeerConnection.IceConnectionState newState) {
                Log.d(TAG, "ICE connection state: " + newState);
                if (newState == PeerConnection.IceConnectionState.CONNECTED) {
                    if (connectionListener != null) connectionListener.onConnectionEstablished();
                } else if (newState == PeerConnection.IceConnectionState.DISCONNECTED) {
                    Log.w(TAG, "ICE Disconnected");
                    if (connectionListener != null) connectionListener.onDisconnected();
                } else if (newState == PeerConnection.IceConnectionState.FAILED) {
                    Log.e(TAG, "ICE Connection Failed");
                    if (connectionListener != null) connectionListener.onDisconnected();
                }
            }

            @Override
            public void onDataChannel(DataChannel dc) {
                Log.d(TAG, "Data channel received: " + dc.label());
                handleDataChannel(dc);
            }

            @Override
            public void onAddStream(MediaStream stream) {
                Log.d(TAG, "onAddStream: " + stream.getId());
                // Unified Plan 下建议使用 onTrack，但为了兼容性也可以处理
                if (!stream.videoTracks.isEmpty()) {
                    VideoTrack videoTrack = stream.videoTracks.get(0);
                    setupRemoteVideoTrack(videoTrack);
                }
            }

            @Override
            public void onRemoveStream(MediaStream stream) {}

            @Override
            public void onTrack(RtpReceiver receiver, MediaStream[] streams) {
                MediaStreamTrack track = receiver.track();
                if (track instanceof VideoTrack) {
                    Log.d(TAG, "onTrack (video)");
                    setupRemoteVideoTrack((VideoTrack) track);
                }
            }
        });

        peerConnection = peerConnectionFactory.createPeerConnection(config, peerObserver);

        // 使用 Unified Plan 方式请求视频流 (仅接收)
        peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
                new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY));

        // 创建 DataChannel
        DataChannel.Init dcInit = new DataChannel.Init();
        dcInit.ordered = true;
        dataChannel = peerConnection.createDataChannel(DATA_CHANNEL_LABEL, dcInit);
        handleDataChannel(dataChannel);

        // 新建"自编码"视频透传通道（无序、不可靠，以最低延迟传输裸 H.264 码流）。
        // 必须在 setLocalDescription 之前创建，才会进入 SDP。
        DataChannel.Init vInit = new DataChannel.Init();
        vInit.ordered = false;
        vInit.maxRetransmits = 0;
        videoDataChannel = peerConnection.createDataChannel(VIDEO_CHANNEL_LABEL, vInit);
        setupVideoDataChannel(videoDataChannel);

        // 保存远端视频渲染器
        this.remoteSurfaceView = remoteRenderer;

        // 创建 Offer
        MediaConstraints constraints = new MediaConstraints();
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false"));

        peerConnection.createOffer(new AppSdpObserver(new AppSdpObserver.SdpEventListener() {
            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                peerConnection.setLocalDescription(new AppSdpObserver(new AppSdpObserver.SdpEventListener() {
                    @Override
                    public void onCreateSuccess(SessionDescription s) {}
                    @Override
                    public void onSetSuccess() {
                        sendOffer(sdp.description, controlledId);
                    }
                    @Override
                    public void onCreateFailure(String error) {}
                    @Override
                    public void onSetFailure(String error) {}
                }), sdp);
            }

            @Override
            public void onSetSuccess() {}
            @Override
            public void onCreateFailure(String error) {
                if (connectionListener != null) connectionListener.onConnectionFailed(error);
            }
            @Override
            public void onSetFailure(String error) {}
        }), constraints);
    }

    public void handleAnswer(String answerSdp) {
        if (peerConnection != null) {
            SessionDescription remoteSdp = new SessionDescription(SessionDescription.Type.ANSWER, answerSdp);
            peerConnection.setRemoteDescription(new AppSdpObserver(new AppSdpObserver.SdpEventListener() {
                @Override
                public void onCreateSuccess(SessionDescription sdp) {}
                @Override
                public void onSetSuccess() {
                    Log.d(TAG, "Remote answer set successfully");
                }
                @Override
                public void onCreateFailure(String error) {}
                @Override
                public void onSetFailure(String error) {}
            }), remoteSdp);
        }
    }

    public void addIceCandidate(IceCandidate candidate) {
        if (peerConnection != null) {
            peerConnection.addIceCandidate(candidate);
        }
    }

    public void sendControlCommand(ControlMessage message) {
        if (isDataChannelOpen()) {
            DataChannel.Buffer buffer = new DataChannel.Buffer(
                    ByteBuffer.wrap(message.toByteArray()), false);
            dataChannel.send(buffer);
        }
    }

    /**
     * 请求被控端切换屏幕采集分辨率（通过 DataChannel 发送 SET_RESOLUTION 指令）。
     * 语义：width <= 0 表示被控端原生分辨率；height <= 0 时按 width 指定长边依屏幕宽高比缩放；
     * fps <= 0 表示沿用当前帧率。
     */
    public void requestResolutionChange(int width, int height, int fps) {
        if (isDataChannelOpen()) {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.SET_RESOLUTION)
                    .setWidth(width)
                    .setHeight(height)
                    .setFps(fps)
                    .build();
            sendControlCommand(msg);
        }
    }

    public void close() {
        if (remoteVideoTrack != null) {
            if (remoteSurfaceView != null) {
                try {
                    remoteVideoTrack.removeSink(remoteSurfaceView);
                } catch (Exception ignored) {
                }
            }
            if (recorderSink != null) {
                try {
                    remoteVideoTrack.removeSink(recorderSink);
                } catch (Exception ignored) {
                }
            }
            remoteVideoTrack = null;
        }
        if (dataChannel != null) {
            try { dataChannel.unregisterObserver(); } catch (Exception ignored) {}
            dataChannel.close();
            dataChannel.dispose();
            dataChannel = null;
        }
        if (videoDataChannel != null) {
            try { videoDataChannel.unregisterObserver(); } catch (Exception ignored) {}
            videoDataChannel.close();
            videoDataChannel.dispose();
            videoDataChannel = null;
        }
        if (peerConnection != null) {
            peerConnection.close();
            peerConnection.dispose();
            peerConnection = null;
        }
        if (peerConnectionFactory != null) {
            peerConnectionFactory.dispose();
            peerConnectionFactory = null;
        }
    }

    private void setupRemoteVideoTrack(VideoTrack videoTrack) {
        if (remoteSurfaceView != null) {
            this.remoteVideoTrack = videoTrack;
            remoteVideoTrack.setEnabled(true);
            remoteVideoTrack.addSink(remoteSurfaceView);
            if (recorderSink != null && recording) {
                remoteVideoTrack.addSink(recorderSink);
            }
            Log.d(TAG, "Remote video track attached to renderer");
        }
    }

    private void handleDataChannel(DataChannel dc) {
        if (dc == null) return;
        // 关键修复：区分控制端创建的两条通道。
        // 虽然本端是创建方，但 PeerObserver.onDataChannel 仍可能在某些 WebRTC 协商逻辑中被触发。
        if (VIDEO_CHANNEL_LABEL.equals(dc.label())) {
            this.videoDataChannel = dc;
            setupVideoDataChannel(dc);
            return;
        }
        this.dataChannel = dc;
        dc.registerObserver(new DataChannel.Observer() {
            @Override
            public void onBufferedAmountChange(long previousAmount) {}

            @Override
            public void onStateChange() {
                try {
                    DataChannel.State state = dc.state();
                    Log.d(TAG, "DataChannel state: " + state);
                } catch (IllegalStateException e) {
                    Log.w(TAG, "DataChannel state error (already disposed?)");
                }
            }

            @Override
            public void onMessage(DataChannel.Buffer buffer) {
                if (buffer == null || buffer.data == null) return;
                byte[] data = new byte[buffer.data.remaining()];
                buffer.data.get(data);
                // 收到被控端回报的消息，按 Action 分发处理。
                try {
                    ControlMessage msg = ControlMessage.parseFrom(data);
                    if (msg.getAction() == Action.REPORT_STREAM_MODE) {
                        if (streamModeReportListener != null) {
                            streamModeReportListener.onStreamModeReported(msg.getStreamMode());
                        }
                    } else if (msg.getAction() == Action.REPORT_RESOLUTION) {
                        Log.i(TAG, "Received resolution report: " + msg.getWidth() + "x" + msg.getHeight()
                                + " @ " + msg.getFps() + "fps, supportedFps=" + msg.getSupportedFpsList());
                        if (selfCodecDecoder != null) {
                            selfCodecDecoder.updateResolution(msg.getWidth(), msg.getHeight());
                        }
                        if (resolutionReportListener != null) {
                            resolutionReportListener.onResolutionReported(
                                    msg.getWidth(), msg.getHeight(), msg.getFps(),
                                    new ArrayList<>(msg.getSupportedFpsList()));
                        }
                    }
                } catch (Exception ignored) {
                }
            }
        });
    }

    private void setupVideoDataChannel(DataChannel dc) {
        dc.registerObserver(new DataChannel.Observer() {
            @Override
            public void onBufferedAmountChange(long previousAmount) {
            }

            @Override
            public void onStateChange() {
                try {
                    DataChannel.State state = dc.state();
                    Log.d(TAG, "Video DataChannel state: " + state);
                } catch (IllegalStateException e) {
                    Log.w(TAG, "Video DataChannel state error (already disposed?)");
                }
            }

            @Override
            public void onMessage(DataChannel.Buffer buffer) {
                if (selfCodecDecoder != null && buffer != null && buffer.data != null) {
                    selfCodecDecoder.onBinaryMessage(buffer.data);
                }
            }
        });
    }

    public void setSelfCodecDecoder(SelfCodecDecoder decoder) {
        this.selfCodecDecoder = decoder;
    }

    /** 设置视频录制器（实现 VideoSink 即可接入远端视频轨道）。 */
    public void setRecorder(VideoSink recorder) {
        this.recorderSink = recorder;
    }

    /** 开启/关闭录制。开启时会将录制器作为额外 sink 接入当前远端视频轨道；
     *  若轨道尚未建立，则在本端收到轨道时（setupRemoteVideoTrack）自动接入。 */
    public void setRecording(boolean on) {
        this.recording = on;
        if (recorderSink == null || remoteVideoTrack == null) return;
        try {
            remoteVideoTrack.removeSink(recorderSink);
        } catch (Exception ignored) {
        }
        if (on) {
            remoteVideoTrack.addSink(recorderSink);
        }
    }

    public void setStreamModeReportListener(StreamModeReportListener l) {
        this.streamModeReportListener = l;
    }

    public boolean isVideoDataChannelOpen() {
        DataChannel dc = videoDataChannel;
        if (dc == null) return false;
        try {
            return dc.state() == DataChannel.State.OPEN;
        } catch (IllegalStateException e) {
            return false;
        }
    }

    /** 请求被控端切换屏幕串流模式（WebRTC / 自编码）。 */
    public void sendStreamMode(int mode) {
        ControlMessage msg = ControlMessage.newBuilder()
                .setAction(Action.SET_STREAM_MODE)
                .setStreamMode(mode)
                .build();
        sendControlCommand(msg);
    }

    private void sendOffer(String sdp, String controlledId) {
        Log.e(TAG, "sendOffer: " );
        SignalMessage msg = new SignalMessage();
        msg.setType("OFFER");
        msg.setFromDeviceId(deviceId);
        msg.setToDeviceId(controlledId);
        msg.setDeviceType("CONTROLLER");
        if (pendingAuthType != null) msg.setAuthType(pendingAuthType);
        if (pendingAuthValue != null) msg.setAuthValue(pendingAuthValue);

        JsonObject payload = new JsonObject();
        payload.addProperty("sdp", sdp);
        msg.setPayload(payload.toString());

        wsClient.sendMessage(msg);
    }

    private void sendIceCandidate(IceCandidate candidate, String controlledId) {
        Log.d(TAG, "sendIceCandidate: " );
        SignalMessage msg = new SignalMessage();
        msg.setType("ICE_CANDIDATE");
        msg.setFromDeviceId(deviceId);
        msg.setToDeviceId(controlledId);
        msg.setDeviceType("CONTROLLER");

        JsonObject payload = new JsonObject();
        payload.addProperty("sdpMid", candidate.sdpMid);
        payload.addProperty("sdpMLineIndex", candidate.sdpMLineIndex);
        payload.addProperty("candidate", candidate.sdp);
        msg.setPayload(payload.toString());

        wsClient.sendMessage(msg);
    }

    public void getStats(org.webrtc.RTCStatsCollectorCallback callback) {
        if (peerConnection != null) {
            peerConnection.getStats(callback);
        }
    }

    /// 控制 DataChannel 是否已打开（用于状态显示）。
    public boolean isDataChannelOpen() {
        DataChannel dc = dataChannel;
        if (dc == null) return false;
        try {
            return dc.state() == DataChannel.State.OPEN;
        } catch (IllegalStateException e) {
            return false;
        }
    }
}
