import { Peer as PeerJs } from 'peerjs'
import type { DataConnection } from 'peerjs'
import { fileMgrInstance, type FileData, type FileInfo } from './fileMgr'
import { NEED_CHUNK_FILE_SIZE, NEED_CHUNK_FILE_SIZE_PREVIEW, TransferStatus, TransferTask } from './fileTransfer'
import { notification } from 'ant-design-vue'
import { randomChars, sign2peerid, getPermissionSet } from './common'

// 发送超时时间（毫秒）
const SEND_TIMEOUT = 10000;

class Peer extends EventTarget {
    peer: PeerJs
    //被动连接
    connections: Map<string, DataConnection> = new Map()
    //主动连接
    remoteConnection: DataConnection | null = null
    //我的id
    id: string | null = null
    //自定义标识
    sign: string | null = null
    constructor(sign: string) {
        super()
        this.sign = sign
    }
    public init() {
        this.peer = new PeerJs(sign2peerid(this.sign))
        this.peer.on('open', (id) => {
            console.log('peer open', id)
            this.id = id
            this.dispatchEvent(new CustomEvent('open', { detail: this.sign }))
        })

        this.peer.on('connection', (conn) => {
            console.log('peer connection', conn)
            this.setupConnection(conn)
        })

        this.peer.on('error', (err) => {
            console.log('peer error', err)
            this.dispatchEvent(new CustomEvent('error', { detail: err }))
        })

        this.peer.on('close', () => {
            console.log('peer close')
            this.dispatchEvent(new CustomEvent('close'))
        })

        this.peer.on('disconnected', () => {
            console.log('peer disconnected')
            this.dispatchEvent(new CustomEvent('disconnected'))
        })
    }

    private setupConnection(conn: DataConnection) {
        this.connections.set(conn.peer, conn)
        conn.on('data', (data: unknown) => {
            const typedData = data as Message

            if (this.callbackMap.has(typedData.id)) {
                this.callbackMap.get(typedData.id)?.(typedData)
                return
            }

            this.handleMessage(typedData, conn)

            this.dispatchEvent(new CustomEvent(typedData.type, {
                detail: { peerId: conn.peer, data: typedData }
            }))
        })
        conn.on('open', () => {
            this.dispatchEvent(new CustomEvent('connection-open', {
                detail: { peer: conn.peer, conn: conn }
            }))
        })
        conn.on('close', () => {
            this.connections.delete(conn.peer)
            this.dispatchEvent(new CustomEvent('peer-disconnected', {
                detail: { peer: conn.peer, conn: conn }
            }))
        })
    }

    connect(id: string) {
        const conn = this.peer.connect(sign2peerid(id))
        this.setupConnection(conn)
        this.remoteConnection = conn
        this.checkConnection()
        return conn
    }
    private callbackMap: Map<string, (data: any) => void> = new Map()
    async send(data: Message, conn: DataConnection = this.remoteConnection, isHandleResponse: boolean = false): Promise<any> {
        data.id = data.id || uuidv4()
        if (!conn) {
            notification.error({
                message: '请创建连接',
            })
            return Promise.reject('连接不存在')
        }
        conn.send(data, true)
        if (isHandleResponse) {
            return Promise.resolve(data.data)
        } else {
            return new Promise((resolve, reject) => {
                const timeoutId = setTimeout(() => {
                    this.callbackMap.delete(data.id)
                    notification.error({
                        message: '返回超时',
                        description: '对方可能已离线或网络异常'
                    })
                    reject(new Error('返回超时'))
                }, SEND_TIMEOUT)
                this.callbackMap.set(data.id, (data: Message) => {
                    clearTimeout(timeoutId)
                    if (data.type === MessageType.response_getFile && data.data.sendType == 'chunk') {
                        //分片传输 特殊处理 可以长时间等待进度
                        return
                    }
                    if (data.type === MessageType.error) {
                        reject(data.data)
                    } else {
                        resolve(data.data)
                    }
                    this.callbackMap.delete(data.id)
                })
            })
        }
    }
    /**共交换的字节数 */
    public transbytesNum: number = 0;
    /**共交换的包数 */
    public transpackNum: number = 0;
    private checkConnection() {
        if (!this.remoteConnection) {
            return
        }
        const rtcp: RTCPeerConnection = this.remoteConnection.peerConnection;
        rtcp.getStats().then((stats) => {
            stats.forEach((stat) => {
                if (stat.type == "data-channel") {
                    //流量
                    this.transbytesNum = stat.bytesReceived + stat.bytesSent;
                    //包数
                    this.transpackNum = stat.messagesReceived + stat.messagesSent;
                }
            })
        })
        setTimeout(() => {
            this.checkConnection()
        }, 1000);
    }

    // 便捷方法用于添加事件监听器
    on(event: string, callback: EventListener) {
        this.addEventListener(event, callback)
    }

    // 便捷方法用于移除事件监听器
    off(event: string, callback: EventListener) {
        this.removeEventListener(event, callback)
    }

    async handleMessage(data: Message, conn: DataConnection) {
        const remoteD = data.data;
        let file: FileInfo | null = null;
        if (data.type == MessageType.error) {
            console.error('handleMessage recive error', data.data)
            return
        }
        let resData: Message = {
            type: MessageType.error,
            data: null,
            id: data.id
        }
        try {
            // 权限校验
            /** 如果通过权限校验，则permissionNo为null */
            let permissionNo: Permission | null = null;
            for (const [permission, allowed] of Object.entries(getPermissionSet())) {
                if (PermissionLimit[permission as Permission].includes(data.type)) {
                    if (allowed) {
                        permissionNo = null;
                        break;
                    } else {
                        permissionNo = permission as Permission;
                    }
                }
            }
            if (permissionNo) {
                throw new Error(`没有权限执行该操作:${data.type},请检查权限${permissionNo}设置`)
            }

            switch (data.type) {
                case MessageType.request_copyClipboard:
                    resData.type = MessageType.response_copyClipboard;
                    //读取粘贴板数据
                    resData.data = await navigator.clipboard.readText().then(text => {
                        return text
                    }).catch(err => {
                        return '没有粘贴板权限或窗口未聚焦，无法复制'
                    });
                    break;
                case MessageType.request_fileInfo:
                    resData.type = MessageType.response_fileInfo;
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path);
                    if (file) {
                        await file.loadLocalDirectory()
                        const json = file.toJson()
                        resData.data = json
                    } else {
                        resData.data = null
                    }
                    break;
                case MessageType.request_fileSize:
                    resData.type = MessageType.response_fileSize
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    resData.data = await file.getFileSize()
                    break;
                case MessageType.request_deleteFile:
                    resData.type = MessageType.response_deleteFile
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    await file.deleteItself()
                    break;
                case MessageType.request_renameFile:
                    resData.type = MessageType.response_renameFile
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    await file.renameFile(remoteD.newName)
                    break;
                case MessageType.request_createDirectory:
                    resData.type = MessageType.response_createDirectory
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    await file.createDirectory(remoteD.name)
                    break;
                case MessageType.request_createFile:
                    resData.type = MessageType.response_createFile
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    await file.createFile(remoteD.name, remoteD.buffer)
                    break;
                case MessageType.request_saveFile:
                    resData.type = MessageType.response_saveFile
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    await file.saveFile(remoteD.buffer)
                    break;
                case MessageType.request_getFileLastModified:
                    resData.type = MessageType.response_getFileLastModified
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    resData.data = await file.getFileLastModified()
                    break;
                case MessageType.request_getFile:
                    if (!remoteD.preView) {
                        //特殊处理下载权限
                        if (!getPermissionSet()[Permission.download]) {
                            throw new Error(`没有权限执行该操作:${data.type},请检查权限${Permission.download}设置`)
                        }
                    }
                    resData.type = MessageType.response_getFile
                    file = (await fileMgrInstance.getRootFile()).getFileInfo(remoteD.path)
                    let fileData = await file.getFile(remoteD.preView) as FileData
                    if (fileData.size > NEED_CHUNK_FILE_SIZE) {
                        this.send({
                            type: MessageType.response_getFile,
                            data: {
                                sendType: 'chunk'
                            },
                            id: data.id
                        }, conn, true)
                        //分片传输
                        if (remoteD.preView) {
                            if (fileData.size < NEED_CHUNK_FILE_SIZE_PREVIEW) {
                                await file.sendFileChunk(conn, remoteD.preView)
                            }
                        } else {
                            await file.sendFileChunk(conn, false, remoteD.savePath)
                        }
                        fileData.buffer = null;
                    } else {
                        //直接传输
                        file.getTransfer(conn).addTask(new TransferTask(fileData, file)).updateFileData(fileData, TransferStatus.COMPLETED)
                    }
                    resData.data = fileData
                    break;
                case MessageType.push_file_chunk:
                    resData.type = MessageType.response_push_file_chunk
                    let fData = remoteD as FileData
                    if (fData.preView) {
                        await fileMgrInstance.remoteRootFile.getFileInfo(fData.path).getTransfer(conn).receiveFile(fData, true)
                    } else {
                        await (await fileMgrInstance.getRootFile()).getTransfer(conn).receiveFile(fData)
                    }
                    resData.data = 'ok'
                    break;
                case MessageType.push_file_complete:
                    resData.type = MessageType.response_push_file_complete
                    console.log('push_file_complete', remoteD)
                    if (remoteD.preView) {
                        notification.success({
                            message: '文件缓存建立完成',
                        })
                    } else {
                        notification.success({
                            message: '文件流式传输完成',
                            description: remoteD.name
                        })
                    }
                    resData.data = 'ok'
                    break;
                default:
                    resData.type = MessageType.error
                    resData.data = '未知消息类型'
                    break;
            }
        } catch (err: any) {
            console.error('handleMessage error', err)
            resData.type = MessageType.error
            resData.data = err.message || JSON.stringify(err)
        }
        this.send(resData, conn, true)
    }
}

export const peer = new Peer(randomChars(6))
export interface Message {
    type: MessageType
    data: any
    id?: string  //uuid
}
export enum MessageType {
    request_copyClipboard = 'request_copyClipboard',
    response_copyClipboard = 'response_copyClipboard',
    request_getFile = 'request_getFile',
    response_getFile = 'response_getFile',
    request_getFileLastModified = 'request_getFileLastModified',
    response_getFileLastModified = 'response_getFileLastModified',
    request_saveFile = 'request_saveFile',
    response_saveFile = 'response_saveFile',
    request_createFile = 'request_createFile',
    response_createFile = 'response_createFile',
    request_createDirectory = 'request_createDirectory',
    response_createDirectory = 'response_createDirectory',
    request_renameFile = 'request_renameFile',
    response_renameFile = 'response_renameFile',
    request_deleteFile = 'request_deleteFile',
    response_deleteFile = 'response_deleteFile',
    error = 'handle_error',
    request_fileSize = 'request_fileSize',
    response_fileSize = 'response_fileSize',
    response_fileInfo = 'response_fileInfo',
    request_fileInfo = 'request_fileInfo',
    push_file_chunk = 'push_file_chunk',
    push_file_complete = 'push_file_complete',
    response_push_file_chunk = 'response_push_file_chunk',
    response_push_file_complete = 'response_push_file_complete',
}
export enum Permission {
    edit = 'edit',
    view = 'view',
    download = 'download',
}
export const PermissionLimit = {
    [Permission.edit]: [MessageType.request_saveFile, MessageType.request_createFile, MessageType.request_createDirectory, MessageType.request_renameFile, MessageType.request_deleteFile],
    [Permission.view]: [MessageType.request_copyClipboard, MessageType.request_getFile, MessageType.request_getFileLastModified, MessageType.request_fileInfo, MessageType.request_fileSize],
    [Permission.download]: [MessageType.request_getFile],
}
export function uuidv4(): string {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}