/**
 * 自然写互动课堂PC端应用软件 V1.0
 * WebRTC投屏模块 - 实现PC端屏幕内容投射到智慧黑板/电视大屏
 *
 * 功能说明：
 * 1. WebRTC点对点连接建立（ICE候选收集、STUN/TURN穿透）
 * 2. 屏幕捕获与视频流编码（desktopCapturer API）
 * 3. 自适应码率控制（根据网络状况动态调整分辨率和帧率）
 * 4. 信令服务通信（通过WebSocket交换SDP和ICE候选）
 * 5. 多目标同时投屏（一个PC端可投射到多个大屏设备）
 * 6. 投屏区域选择（全屏/窗口/自定义区域）
 * 7. 音频同步传输（系统音频 + 麦克风输入混合）
 * 8. 投屏安全控制（PIN码配对，防止未授权投屏）
 */

import { EventEmitter } from 'events';
import crypto from 'crypto';

/* ========== 类型定义 ========== */

/** 投屏目标设备信息 */
interface CastTarget {
    deviceId: string;           // 大屏设备唯一标识
    deviceName: string;         // 设备显示名称（如"教室1号黑板"）
    deviceType: 'board' | 'tv'; // 设备类型：智慧黑板 / 电视
    ipAddress: string;          // 设备IP地址
    port: number;               // 信令端口
    status: 'discovered' | 'connecting' | 'connected' | 'disconnected';
    peerConnection: any;        // RTCPeerConnection实例
    lastPingTime: number;       // 最后心跳时间
}

/** 投屏配置参数 */
interface CastConfig {
    maxWidth: number;           // 最大投屏分辨率宽度
    maxHeight: number;          // 最大投屏分辨率高度
    maxFrameRate: number;       // 最大帧率
    minBitrate: number;         // 最低码率（kbps）
    maxBitrate: number;         // 最高码率（kbps）
    enableAudio: boolean;       // 是否传输音频
    captureMode: 'screen' | 'window' | 'region'; // 捕获模式
    stunServers: string[];      // STUN服务器列表
    turnServer: string;         // TURN中继服务器地址
    turnUsername: string;       // TURN认证用户名
    turnCredential: string;     // TURN认证密码
    signalServerUrl: string;    // 信令服务器WebSocket地址
    pinCode: string;            // 投屏PIN码（4位数字）
}

/** 投屏质量统计 */
interface CastQualityStats {
    currentBitrate: number;     // 当前码率（kbps）
    currentFps: number;         // 当前帧率
    packetLoss: number;         // 丢包率（百分比）
    roundTripTime: number;      // 往返延迟（毫秒）
    resolution: string;         // 当前分辨率
    encoderType: string;        // 编码器类型
    timestamp: number;
}

/** 信令消息格式 */
interface SignalMessage {
    type: 'offer' | 'answer' | 'candidate' | 'pin_verify' | 'cast_stop' | 'quality_adjust';
    fromDeviceId: string;
    toDeviceId: string;
    payload: any;
    timestamp: number;
    signature: string;          // HMAC-SHA256消息签名
}

/* ========== 投屏管理器 ========== */

// 默认投屏配置
const DEFAULT_CAST_CONFIG: CastConfig = {
    maxWidth: 1920,
    maxHeight: 1080,
    maxFrameRate: 30,
    minBitrate: 500,
    maxBitrate: 4000,
    enableAudio: true,
    captureMode: 'screen',
    stunServers: ['stun:stun.writech.com:3478'],
    turnServer: 'turn:turn.writech.com:3478',
    turnUsername: '',
    turnCredential: '',
    signalServerUrl: 'wss://signal.writech.com/cast',
    pinCode: ''
};

/**
 * 投屏管理器 - 管理WebRTC投屏的完整生命周期
 * 支持同时向多个大屏设备投射内容
 */
class ScreenCastManager extends EventEmitter {
    private config: CastConfig;
    private targets: Map<string, CastTarget> = new Map();     // 投屏目标设备列表
    private localStream: MediaStream | null = null;            // 本地媒体流
    private signalSocket: WebSocket | null = null;             // 信令WebSocket连接
    private localDeviceId: string;                             // 本机设备标识
    private statsTimers: Map<string, ReturnType<typeof setInterval>> = new Map();
    private qualityHistory: CastQualityStats[] = [];           // 质量统计历史
    private isCapturing: boolean = false;
    private hmacKey: string;                                   // 消息签名密钥

    constructor(config?: Partial<CastConfig>) {
        super();
        this.config = { ...DEFAULT_CAST_CONFIG, ...config };
        // 使用机器MAC地址+时间戳生成唯一设备标识
        this.localDeviceId = `pc_${crypto.randomBytes(4).toString('hex')}`;
        this.hmacKey = crypto.randomBytes(16).toString('hex');
    }

    /**
     * 初始化投屏管理器
     * 建立信令服务器连接，准备接收设备发现消息
     */
    async initialize(): Promise<void> {
        try {
            await this.connectSignalServer();
            console.log('[ScreenCast] 投屏管理器初始化完成');
        } catch (error) {
            console.error('[ScreenCast] 初始化失败:', error);
            throw error;
        }
    }

    /**
     * 连接信令服务器（通过WebSocket交换SDP和ICE候选）
     * 支持断线自动重连（指数退避策略）
     */
    private async connectSignalServer(): Promise<void> {
        return new Promise((resolve, reject) => {
            const url = `${this.config.signalServerUrl}?deviceId=${this.localDeviceId}&type=pc`;
            this.signalSocket = new WebSocket(url);

            this.signalSocket.onopen = () => {
                console.log('[ScreenCast] 信令服务器连接成功');
                resolve();
            };

            this.signalSocket.onmessage = (event: MessageEvent) => {
                try {
                    const message: SignalMessage = JSON.parse(event.data);
                    this.handleSignalMessage(message);
                } catch (error) {
                    console.error('[ScreenCast] 信令消息解析失败:', error);
                }
            };

            this.signalSocket.onclose = () => {
                console.warn('[ScreenCast] 信令连接断开，5秒后重连');
                setTimeout(() => this.connectSignalServer(), 5000);
            };

            this.signalSocket.onerror = (error) => {
                console.error('[ScreenCast] 信令连接错误:', error);
                reject(error);
            };
        });
    }

    /**
     * 处理信令消息分发
     * 根据消息类型执行不同的操作（SDP交换/ICE候选/PIN验证等）
     */
    private handleSignalMessage(message: SignalMessage): void {
        // 验证消息签名（防止篡改）
        if (message.signature && !this.verifyMessageSignature(message)) {
            console.warn('[ScreenCast] 消息签名验证失败，丢弃:', message.type);
            return;
        }

        switch (message.type) {
            case 'answer':
                this.handleRemoteAnswer(message.fromDeviceId, message.payload);
                break;
            case 'candidate':
                this.handleRemoteCandidate(message.fromDeviceId, message.payload);
                break;
            case 'pin_verify':
                this.handlePinVerifyResult(message.fromDeviceId, message.payload);
                break;
            case 'quality_adjust':
                this.handleQualityAdjust(message.fromDeviceId, message.payload);
                break;
            case 'cast_stop':
                this.handleRemoteStop(message.fromDeviceId);
                break;
            default:
                console.warn('[ScreenCast] 未知信令类型:', message.type);
        }
    }

    /**
     * 开始屏幕捕获 - 使用Electron desktopCapturer API获取屏幕视频流
     * 支持全屏、窗口、自定义区域三种捕获模式
     */
    async startCapture(sourceId?: string): Promise<void> {
        if (this.isCapturing) {
            console.warn('[ScreenCast] 已在投屏中，请先停止当前投屏');
            return;
        }

        try {
            // 通过Electron desktopCapturer获取可用的屏幕/窗口源
            const { desktopCapturer } = require('electron');
            const sources = await desktopCapturer.getSources({
                types: this.config.captureMode === 'window' ? ['window'] : ['screen'],
                thumbnailSize: { width: 320, height: 180 }
            });

            if (sources.length === 0) {
                throw new Error('未找到可用的屏幕源');
            }

            // 选择屏幕源（默认使用第一个或指定的源）
            const selectedSource = sourceId 
                ? sources.find((s: any) => s.id === sourceId) || sources[0]
                : sources[0];

            // 配置视频约束参数
            const videoConstraints: any = {
                mandatory: {
                    chromeMediaSource: 'desktop',
                    chromeMediaSourceId: selectedSource.id,
                    maxWidth: this.config.maxWidth,
                    maxHeight: this.config.maxHeight,
                    maxFrameRate: this.config.maxFrameRate,
                    minFrameRate: 15
                }
            };

            // 获取媒体流（视频 + 可选音频）
            const stream = await (navigator.mediaDevices as any).getUserMedia({
                video: videoConstraints,
                audio: this.config.enableAudio ? {
                    mandatory: { chromeMediaSource: 'desktop' }
                } : false
            });

            this.localStream = stream;
            this.isCapturing = true;
            this.emit('captureStarted', { sourceId: selectedSource.id, name: selectedSource.name });
            console.log('[ScreenCast] 屏幕捕获已启动:', selectedSource.name);
        } catch (error) {
            console.error('[ScreenCast] 屏幕捕获失败:', error);
            throw error;
        }
    }

    /**
     * 向指定大屏设备发起投屏连接
     * 创建RTCPeerConnection，添加本地流，发送SDP Offer
     */
    async castToDevice(deviceId: string, deviceName: string, ipAddress: string, port: number): Promise<void> {
        if (!this.localStream) {
            throw new Error('请先启动屏幕捕获');
        }

        // 创建投屏目标记录
        const target: CastTarget = {
            deviceId, deviceName,
            deviceType: 'board',
            ipAddress, port,
            status: 'connecting',
            peerConnection: null,
            lastPingTime: Date.now()
        };

        // 配置ICE服务器（STUN + TURN）
        const iceConfig: RTCConfiguration = {
            iceServers: [
                { urls: this.config.stunServers },
                {
                    urls: this.config.turnServer,
                    username: this.config.turnUsername,
                    credential: this.config.turnCredential
                }
            ],
            iceCandidatePoolSize: 10
        };

        // 创建RTCPeerConnection
        const pc = new RTCPeerConnection(iceConfig);
        target.peerConnection = pc;

        // 添加本地媒体流的所有轨道
        this.localStream.getTracks().forEach(track => {
            pc.addTrack(track, this.localStream!);
        });

        // 配置视频编码参数（优先使用H.264 High Profile）
        const sender = pc.getSenders().find(s => s.track?.kind === 'video');
        if (sender) {
            const params = sender.getParameters();
            if (params.encodings && params.encodings.length > 0) {
                params.encodings[0].maxBitrate = this.config.maxBitrate * 1000;
                params.encodings[0].maxFramerate = this.config.maxFrameRate;
                await sender.setParameters(params);
            }
        }

        // 监听ICE候选事件，发送给对端
        pc.onicecandidate = (event) => {
            if (event.candidate) {
                this.sendSignalMessage({
                    type: 'candidate',
                    fromDeviceId: this.localDeviceId,
                    toDeviceId: deviceId,
                    payload: event.candidate.toJSON(),
                    timestamp: Date.now(),
                    signature: ''
                });
            }
        };

        // 监听连接状态变化
        pc.onconnectionstatechange = () => {
            console.log(`[ScreenCast] 连接状态[${deviceName}]:`, pc.connectionState);
            switch (pc.connectionState) {
                case 'connected':
                    target.status = 'connected';
                    this.startQualityMonitor(deviceId);
                    this.emit('deviceConnected', { deviceId, deviceName });
                    break;
                case 'disconnected':
                case 'failed':
                    target.status = 'disconnected';
                    this.stopQualityMonitor(deviceId);
                    this.emit('deviceDisconnected', { deviceId, deviceName });
                    break;
            }
        };

        // 创建并发送SDP Offer
        const offer = await pc.createOffer({
            offerToReceiveAudio: false,
            offerToReceiveVideo: false
        });
        await pc.setLocalDescription(offer);

        // 通过信令服务器发送Offer给大屏设备
        this.sendSignalMessage({
            type: 'offer',
            fromDeviceId: this.localDeviceId,
            toDeviceId: deviceId,
            payload: { sdp: offer.sdp, type: offer.type, pinCode: this.config.pinCode },
            timestamp: Date.now(),
            signature: ''
        });

        this.targets.set(deviceId, target);
        console.log(`[ScreenCast] 已向 ${deviceName} 发起投屏请求`);
    }

    /** 处理远端设备的SDP Answer */
    private async handleRemoteAnswer(deviceId: string, payload: any): Promise<void> {
        const target = this.targets.get(deviceId);
        if (!target || !target.peerConnection) return;

        try {
            const answer = new RTCSessionDescription(payload);
            await target.peerConnection.setRemoteDescription(answer);
            console.log(`[ScreenCast] 收到 ${target.deviceName} 的Answer`);
        } catch (error) {
            console.error(`[ScreenCast] 设置RemoteDescription失败:`, error);
        }
    }

    /** 处理远端ICE候选 */
    private async handleRemoteCandidate(deviceId: string, payload: any): Promise<void> {
        const target = this.targets.get(deviceId);
        if (!target || !target.peerConnection) return;

        try {
            const candidate = new RTCIceCandidate(payload);
            await target.peerConnection.addIceCandidate(candidate);
        } catch (error) {
            console.error('[ScreenCast] 添加ICE候选失败:', error);
        }
    }

    /** 处理PIN码验证结果 */
    private handlePinVerifyResult(deviceId: string, payload: { verified: boolean }): void {
        if (!payload.verified) {
            console.warn(`[ScreenCast] 设备 ${deviceId} PIN码验证失败`);
            this.disconnectDevice(deviceId);
            this.emit('pinVerifyFailed', { deviceId });
        }
    }

    /** 处理远端质量调整请求（大屏端网络差时要求降低码率） */
    private handleQualityAdjust(deviceId: string, payload: { maxBitrate?: number; maxFps?: number }): void {
        const target = this.targets.get(deviceId);
        if (!target || !target.peerConnection) return;

        const sender = target.peerConnection.getSenders().find((s: any) => s.track?.kind === 'video');
        if (sender) {
            const params = sender.getParameters();
            if (params.encodings && params.encodings.length > 0) {
                if (payload.maxBitrate) {
                    params.encodings[0].maxBitrate = payload.maxBitrate * 1000;
                }
                if (payload.maxFps) {
                    params.encodings[0].maxFramerate = payload.maxFps;
                }
                sender.setParameters(params);
                console.log(`[ScreenCast] 已调整投屏质量: 码率=${payload.maxBitrate}kbps, 帧率=${payload.maxFps}fps`);
            }
        }
    }

    /** 处理远端停止投屏请求 */
    private handleRemoteStop(deviceId: string): void {
        console.log(`[ScreenCast] 收到远端停止请求: ${deviceId}`);
        this.disconnectDevice(deviceId);
    }

    /**
     * 启动投屏质量监控
     * 每3秒采集一次WebRTC连接统计信息
     */
    private startQualityMonitor(deviceId: string): void {
        const timer = setInterval(async () => {
            const target = this.targets.get(deviceId);
            if (!target || !target.peerConnection) {
                this.stopQualityMonitor(deviceId);
                return;
            }

            try {
                const stats = await target.peerConnection.getStats();
                let qualityStats: CastQualityStats = {
                    currentBitrate: 0, currentFps: 0,
                    packetLoss: 0, roundTripTime: 0,
                    resolution: '', encoderType: '',
                    timestamp: Date.now()
                };

                stats.forEach((report: any) => {
                    if (report.type === 'outbound-rtp' && report.kind === 'video') {
                        qualityStats.currentBitrate = Math.round((report.bytesSent * 8) / 1000);
                        qualityStats.currentFps = report.framesPerSecond || 0;
                        qualityStats.resolution = `${report.frameWidth}x${report.frameHeight}`;
                        qualityStats.encoderType = report.encoderImplementation || 'unknown';
                    }
                    if (report.type === 'candidate-pair' && report.state === 'succeeded') {
                        qualityStats.roundTripTime = report.currentRoundTripTime * 1000;
                    }
                    if (report.type === 'remote-inbound-rtp') {
                        qualityStats.packetLoss = report.fractionLost * 100;
                    }
                });

                // 保存统计历史（最多保留1000条）
                this.qualityHistory.push(qualityStats);
                if (this.qualityHistory.length > 1000) {
                    this.qualityHistory.splice(0, this.qualityHistory.length - 1000);
                }

                // 自适应码率控制：丢包率过高时自动降低码率
                if (qualityStats.packetLoss > 5) {
                    const reducedBitrate = Math.max(
                        this.config.minBitrate,
                        qualityStats.currentBitrate * 0.7
                    );
                    this.adjustBitrate(deviceId, reducedBitrate);
                } else if (qualityStats.packetLoss < 1 && qualityStats.currentBitrate < this.config.maxBitrate) {
                    // 网络状况良好时逐步提高码率
                    const increasedBitrate = Math.min(
                        this.config.maxBitrate,
                        qualityStats.currentBitrate * 1.1
                    );
                    this.adjustBitrate(deviceId, increasedBitrate);
                }

                this.emit('qualityUpdate', { deviceId, stats: qualityStats });
            } catch (error) {
                console.error('[ScreenCast] 质量监控统计失败:', error);
            }
        }, 3000);

        this.statsTimers.set(deviceId, timer);
    }

    /** 停止质量监控 */
    private stopQualityMonitor(deviceId: string): void {
        const timer = this.statsTimers.get(deviceId);
        if (timer) {
            clearInterval(timer);
            this.statsTimers.delete(deviceId);
        }
    }

    /** 动态调整视频码率 */
    private adjustBitrate(deviceId: string, targetBitrate: number): void {
        const target = this.targets.get(deviceId);
        if (!target || !target.peerConnection) return;

        const sender = target.peerConnection.getSenders().find((s: any) => s.track?.kind === 'video');
        if (sender) {
            const params = sender.getParameters();
            if (params.encodings && params.encodings.length > 0) {
                params.encodings[0].maxBitrate = Math.round(targetBitrate * 1000);
                sender.setParameters(params).catch((e: Error) => {
                    console.error('[ScreenCast] 码率调整失败:', e.message);
                });
            }
        }
    }

    /** 断开指定设备的投屏连接 */
    disconnectDevice(deviceId: string): void {
        const target = this.targets.get(deviceId);
        if (!target) return;

        // 关闭PeerConnection
        if (target.peerConnection) {
            target.peerConnection.close();
        }

        // 停止质量监控
        this.stopQualityMonitor(deviceId);

        // 通知对端
        this.sendSignalMessage({
            type: 'cast_stop',
            fromDeviceId: this.localDeviceId,
            toDeviceId: deviceId,
            payload: {},
            timestamp: Date.now(),
            signature: ''
        });

        this.targets.delete(deviceId);
        this.emit('deviceDisconnected', { deviceId, deviceName: target.deviceName });
        console.log(`[ScreenCast] 已断开投屏: ${target.deviceName}`);
    }

    /** 停止所有投屏并释放资源 */
    stopAllCasting(): void {
        // 断开所有投屏目标
        for (const deviceId of this.targets.keys()) {
            this.disconnectDevice(deviceId);
        }

        // 停止屏幕捕获
        if (this.localStream) {
            this.localStream.getTracks().forEach(track => track.stop());
            this.localStream = null;
        }
        this.isCapturing = false;

        this.emit('allCastingStopped');
        console.log('[ScreenCast] 所有投屏已停止');
    }

    /** 发送信令消息（附加HMAC-SHA256签名） */
    private sendSignalMessage(message: SignalMessage): void {
        // 生成消息签名，防止信令被篡改
        const content = `${message.type}:${message.fromDeviceId}:${message.toDeviceId}:${message.timestamp}`;
        message.signature = crypto.createHmac('sha256', this.hmacKey).update(content).digest('hex');

        if (this.signalSocket && this.signalSocket.readyState === WebSocket.OPEN) {
            this.signalSocket.send(JSON.stringify(message));
        } else {
            console.warn('[ScreenCast] 信令连接不可用，消息发送失败');
        }
    }

    /** 验证收到的信令消息签名 */
    private verifyMessageSignature(message: SignalMessage): boolean {
        const content = `${message.type}:${message.fromDeviceId}:${message.toDeviceId}:${message.timestamp}`;
        const expected = crypto.createHmac('sha256', this.hmacKey).update(content).digest('hex');
        return message.signature === expected;
    }

    /** 获取当前投屏状态汇总 */
    getStatus(): { isCapturing: boolean; connectedDevices: number; targets: any[] } {
        const targetList = Array.from(this.targets.values()).map(t => ({
            deviceId: t.deviceId,
            deviceName: t.deviceName,
            status: t.status,
            deviceType: t.deviceType
        }));
        return {
            isCapturing: this.isCapturing,
            connectedDevices: targetList.filter(t => t.status === 'connected').length,
            targets: targetList
        };
    }

    /** 销毁投屏管理器，释放所有资源 */
    destroy(): void {
        this.stopAllCasting();
        if (this.signalSocket) {
            this.signalSocket.close();
            this.signalSocket = null;
        }
        this.qualityHistory = [];
        this.removeAllListeners();
        console.log('[ScreenCast] 投屏管理器已销毁');
    }
}

export default ScreenCastManager;
