import type { DataConnection } from "peerjs";
import { FileTransfer, TransferStatus, TransferTask } from "./fileTransfer";
import { peer } from "./peer";
import { MessageType } from "./peer";
import { notification } from "ant-design-vue";
import emitter, { EmitterEvent } from "./emitter";


export class FileInfo {
    /**文件句柄 */
    public fileDirHandler: FileSystemDirectoryHandle | FileSystemFileHandle;
    /**父级文件夹 */
    public parentFileInfo: FileInfo | null = null;
    /**文件名 */
    public name: string = '';
    /**是否是文件夹 */
    public isDirectory: boolean = false;
    /**文件路径 */
    public path: string = '';
    /**文件大小 */
    public size: number = 0;
    /**文件最后修改时间 */
    public lastModified: number = 0;
    /**子(文件/目录) */
    public files: FileInfo[] = [];
    /**是否是远程文件 */
    public isRemote: boolean = false;
    /**是否初始化 */
    public isInit: boolean = false;
    /**传输任务 */
    public transfer: FileTransfer | null = null;
    constructor(isRemote: boolean = false) {
        this.isRemote = isRemote;
    }
    public async init(fileHandler: FileSystemDirectoryHandle | FileSystemFileHandle, parentFileInfo: FileInfo | null = null) {
        this.fileDirHandler = fileHandler;
        this.name = fileHandler.name;
        this.isDirectory = fileHandler.kind === "directory";
        this.parentFileInfo = parentFileInfo;
        this.path = parentFileInfo ? `${parentFileInfo.path}/${this.name}` : this.name;
        this.size = await this.getFileSize();
        this.lastModified = await this.getFileLastModified();
        this.isInit = true;
    }
    public async updateRemote(fileInfo: FileInfo) {
        this.isRemote = true;
        this.name = fileInfo.name;
        this.isDirectory = fileInfo.isDirectory;
        this.path = fileInfo.path;
        this.size = fileInfo.size;
        this.lastModified = fileInfo.lastModified;
        this.files = fileInfo.files.map(file => {
            const newFile = new FileInfo();
            newFile.updateRemote(file);
            newFile.parentFileInfo = this;
            return newFile;
        });
        this.isInit = true;
    }
    public addTransfer(transfer: FileTransfer) {
        this.transfer = transfer;
    }
    public getTransfer(conn: DataConnection): FileTransfer {
        if (this.transfer) {
            if (this.transfer.conn !== conn) {
                this.transfer.conn = conn;
            }
            return this.transfer;
        }
        return new FileTransfer(conn, this);
    }
    public getFileInfo(path: string): FileInfo | null {
        if (path === '') return this;
        if (this.path === path) return this;
        if (this.files.length > 0) {
            for (const file of this.files) {
                const info = file.getFileInfo(path);
                if (info) return info;
            }
        }
        return null;
    }

    public toJson(): object {
        return {
            name: this.name,
            isDirectory: this.isDirectory,
            size: this.size,
            lastModified: this.lastModified,
            path: this.path,
            files: this.files.map(file => file.toJson()),
        }
    }

    /**加载目录 */
    public async loadLocalDirectory(sortType: 'name' | 'size' | 'type' | 'date' = 'type', sortOrder: 'asc' | 'desc' = 'asc'): Promise<FileInfo[]> {
        if (this.isRemote) {
            await peer.send({
                type: MessageType.request_fileInfo,
                data: {
                    path: this.path,
                }
            }).then((data: FileInfo) => {
                this.updateRemote(data)
            })
        } else {
            if (!this.isDirectory) return [];
            this.files = [];
            const fileList: FileInfo[] = [];
            try {
                for await (const entry of (this.fileDirHandler as FileSystemDirectoryHandle).values()) {
                    const file = new FileInfo();
                    await file.init(entry, this);
                    fileList.push(file);
                }
            } catch (error) {
                console.error('加载目录失败:', error);
                return [];
            }
            this.files = fileList;
        }

        this.files.sort((a, b) => {
            if (sortType === 'name') return sortOrder === 'asc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
            if (sortType === 'size') return sortOrder === 'asc' ? a.size - b.size : b.size - a.size;
            if (sortType === 'type') return sortOrder === 'asc' ? (a.isDirectory ? -1 : 1) : (a.isDirectory ? 1 : -1);
            if (sortType === 'date') return sortOrder === 'asc' ? a.lastModified - b.lastModified : b.lastModified - a.lastModified;
            return 0;
        });
        emitter.emit(EmitterEvent.update_directory, this);
        return this.files;

    }

    /**获取文件大小 */
    public async getFileSize(): Promise<number> {
        if (this.isDirectory) return 0;
        try {
            if (this.isRemote) {
                await peer.send({
                    type: MessageType.request_fileSize,
                    data: {
                        path: this.path,
                    }
                }).then((data: number) => {
                    this.size = data;
                })
            } else {
                const file = await (this.fileDirHandler as FileSystemFileHandle).getFile();
                this.size = file.size;

            }
            return this.size;
        } catch (error) {
            console.error('获取文件大小失败:', error);
            return 0;
        }
    }

    /**获取文件的MD5值 */
    public async getFileMD5(): Promise<string> {
        if (this.isDirectory) return '';
        const file = await (this.fileDirHandler as FileSystemFileHandle).getFile();
        // 获取文件的MD5值
        // todo
        return '';
    }

    /**删除文件 */
    public async deleteItself() {
        try {
            if (this.parentFileInfo) {
                if (this.isRemote) {
                    await peer.send({
                        type: MessageType.request_deleteFile,
                        data: {
                            path: this.path,
                        }
                    }).then(async () => {
                        await this.parentFileInfo.loadLocalDirectory();
                    }).catch((err) => {
                        throw err;
                    })
                } else {
                    await (this.parentFileInfo.fileDirHandler as FileSystemDirectoryHandle)
                        .removeEntry(this.name, { recursive: true });
                    await this.parentFileInfo.loadLocalDirectory();
                }
            }
        } catch (error) {
            console.error('删除文件失败:', error);
            throw error;
        }
    }

    /**文件重命名 */
    public async renameFile(newName: string) {
        try {
            if (this.parentFileInfo) {
                if (this.isRemote) {
                    await peer.send({
                        type: MessageType.request_renameFile,
                        data: {
                            path: this.path,
                            newName: newName
                        }
                    }).then(async () => {
                        await this.parentFileInfo.loadLocalDirectory();
                    }).catch((err) => {
                        throw err;
                    })
                } else {
                    const parentDir = this.parentFileInfo.fileDirHandler as FileSystemDirectoryHandle;
                    // 创建新文件
                    if (this.isDirectory) {
                        const newDir = await parentDir.getDirectoryHandle(newName, { create: true });
                        // 复制所有文件到新目录
                        await this.copyDirectory(this.fileDirHandler as FileSystemDirectoryHandle, newDir);
                    } else {
                        const file = await (this.fileDirHandler as FileSystemFileHandle).getFile();
                        const newFile = await parentDir.getFileHandle(newName, { create: true });
                        const writable = await newFile.createWritable();
                        await writable.write(await file.arrayBuffer());
                        await writable.close();
                    }
                    // 删除旧文件
                    await parentDir.removeEntry(this.name, { recursive: true });
                    // 更新当前实例
                    this.name = newName;
                    this.path = `${this.parentFileInfo.path}/${newName}`;
                    await this.parentFileInfo.loadLocalDirectory();
                }
            }
        } catch (error) {
            console.error('重命名文件失败:', error);
            throw error;
        }
    }

    /**文件夹复制 */
    private async copyDirectory(source: FileSystemDirectoryHandle, target: FileSystemDirectoryHandle) {
        if (this.isRemote) throw new Error('不能复制远程文件夹');
        for await (const entry of source.values()) {
            if (entry.kind === 'file') {
                const file = await entry.getFile();
                const newFile = await target.getFileHandle(entry.name, { create: true });
                const writable = await newFile.createWritable();
                await writable.write(await file.arrayBuffer());
                await writable.close();
            } else {
                const newDir = await target.getDirectoryHandle(entry.name, { create: true });
                await this.copyDirectory(entry, newDir);
            }
        }
    }
    /**创建路径 */
    public async createPath(path: string): Promise<FileSystemDirectoryHandle> {
        const pathArr = path.split('/');
        let currentPath = this.fileDirHandler as FileSystemDirectoryHandle;
        for (const dir of pathArr) {
            if (dir === '') continue;
            currentPath = await currentPath.getDirectoryHandle(dir, { create: true });
        }
        return currentPath;
    }
    /**创建文件夹 */
    public async createDirectory(name: string) {
        if (!this.isDirectory) throw new Error('不能创建文件夹');
        if (this.isRemote) {
            await peer.send({
                type: MessageType.request_createDirectory,
                data: {
                    path: this.path,
                    name: name
                }
            }).then(async () => {
                await this.loadLocalDirectory();
            }).catch((err) => {
                throw err;
            })
        } else {
            await (this.fileDirHandler as FileSystemDirectoryHandle).getDirectoryHandle(name, { create: true });
            await this.loadLocalDirectory();
        }
    }
    /**预览缓存 */
    public previewCache: Uint8Array;
    /**添加预览缓存 */
    public addPreviewCacheBuffer(buffer: ArrayBuffer, offset: number = 0, totalSize: number = 0) {
        if (!this.previewCache) {
            this.previewCache = new Uint8Array(totalSize);
        }
        this.previewCache.set(new Uint8Array(buffer), offset);
    }
    /**清除预览缓存 */
    public clearPreviewCache() {
        this.previewCache = null;
    }
    /**创建文件
     * @param name 文件名或路径+文件名
     * @param buffer 文件内容
     * @param offset 偏移量
     */
    public async createFile(name: string, buffer: ArrayBuffer, offset: number = 0) {
        if (!this.isDirectory) throw new Error('不能创建文件');
        if (this.isRemote) {
            await peer.send({
                type: MessageType.request_createFile,
                data: {
                    path: this.path,
                    name: name,
                    buffer: buffer,
                    offset: offset
                }
            }).then(async () => {
                await this.loadLocalDirectory();
            }).catch((err) => {
                throw err;
            })
        } else {
            let dir = this.fileDirHandler as FileSystemDirectoryHandle;
            if (name.split('/').length > 1) {
                // 创建深路径文件
                const path = name.split('/').slice(0, -1).join('/');
                name = name.split('/').slice(-1).join('/');
                dir = await this.createPath(path);
            }
            if (offset > 0) {
                const fileHandler = await dir.getFileHandle(name, { create: true });
                const writable = await fileHandler.createWritable({ keepExistingData: true });
                await writable.write({
                    type: 'write',
                    position: offset,
                    data: buffer,
                });
                await writable.close();
            } else {

                const file = await dir.getFileHandle(name, { create: true });
                const writable = await file.createWritable();
                await writable.write(buffer);
                await writable.close();
            }
        }
    }
    /**保存文件 */
    public async saveFile(buffer: ArrayBuffer, offset: number = 0) {
        if (this.isDirectory) {
            throw new Error('不能保存文件夹');
        }
        if (this.isRemote) {
            await peer.send({
                type: MessageType.request_saveFile,
                data: {
                    path: this.path,
                    buffer: buffer,
                    offset: offset
                }
            }).then(async () => {
                await this.loadLocalDirectory();
            }).catch((err) => {
                throw err;
            })
        } else {
            if (offset > 0) {
                const writable = await (this.fileDirHandler as FileSystemFileHandle).createWritable({ keepExistingData: true });
                await writable.write({
                    type: 'write',
                    position: offset,
                    data: buffer,
                });
                await writable.close();
            } else {
                const writable = await (this.fileDirHandler as FileSystemFileHandle).createWritable();
                await writable.write(buffer);
                await writable.close();
            }
        }
    }

    /**获取文件最后修改时间 */
    public async getFileLastModified(): Promise<number> {
        if (this.isDirectory) return 0;
        if (this.isRemote) {
            await peer.send({
                type: MessageType.request_getFileLastModified,
                data: {
                    path: this.path,
                }
            }).then((data: number) => {
                this.lastModified = data;
            })
        } else {
            const file = await (this.fileDirHandler as FileSystemFileHandle).getFile();
            this.lastModified = file.lastModified;
        }
        return this.lastModified;
    }

    /**获取文件
     * @param preView 是否预览
     * @param savePath 在当前路径保存内容（文件/文件夹）
     * @param excludePath 排除路径(生成文件夹路径时排除的选项)
     */
    public async getFile(preView: boolean = false, savePath: string = '', excludePath: string = ''): Promise<FileData | FileData[]> {
        if (this.isDirectory) {
            const fileDatas: FileData[] = [];
            await this.loadLocalDirectory();
            this.files.forEach(async file => {
                const fData = await file.getFile(preView, savePath, excludePath);
                if (Array.isArray(fData)) {
                    fileDatas.push(...fData);
                } else {
                    fileDatas.push(fData);
                }
            })
            return fileDatas;
        } else {
            if (this.isRemote) {
                return await peer.send({
                    type: MessageType.request_getFile,
                    data: {
                        path: this.path,
                        preView: preView,
                        savePath: savePath + '/' + this.path.replace(excludePath, '').replace(this.name, '')
                    }
                }).then(async (data: FileData) => {
                    if (data.buffer && !data.preView) {
                        (await fileMgrInstance.getRootFile()).createFile(savePath + '/' + this.path.replace(excludePath, ''), data.buffer).then(() => {
                            this.getTransfer(peer.remoteConnection).addTask(new TransferTask(data, this)).updateFileData(data, TransferStatus.COMPLETED)
                            notification.success({
                                message: this.name + "文件接收成功",
                            });
                        })
                            .catch((err) => {
                                notification.error({
                                    message: this.name + "文件接收失败",
                                    description: err.message,
                                });
                            });;
                    }
                    return data;
                }).catch((err) => {
                    notification.error({
                        message: this.name + "文件接收失败",
                        description: err.message || err,
                    });
                    throw err;
                })
            } else {
                const file = await (this.fileDirHandler as FileSystemFileHandle).getFile();
                return {
                    type: file.type,
                    buffer: await file.arrayBuffer(),
                    name: file.name,
                    size: file.size,
                    lastModified: file.lastModified,
                    path: this.path,
                    MD5: await this.getFileMD5(),
                    preView: preView
                }
            }
        }
    }

    //分片传输
    public async sendFileChunk(conn: DataConnection, preView: boolean = false, savePath: string = ''): Promise<boolean> {
        return this.getTransfer(conn).init(preView).sendFile(savePath)
    }
}
class fileMgr {
    private _rootFile: FileInfo;
    public remoteRootFile: FileInfo = new FileInfo(true);
    public async selectLocalDirectory() {
        try {
            const dirHandle = await window.showDirectoryPicker({
                id: 'p2p-explorer-web',
                mode: 'readwrite',
                startIn: 'downloads'
            });
            this._rootFile = new FileInfo();
            await this._rootFile.init(dirHandle);
            emitter.emit(EmitterEvent.choosed_local_directory);
        } catch (error) {
            console.error('选择目录失败:', error);
            throw error;
        }
    }
    public async getRootFile(): Promise<FileInfo> {
        if (!this._rootFile) {
            await this.selectLocalDirectory();
        }
        return this._rootFile;
    }
}
export const fileMgrInstance = new fileMgr();
export interface FileData {
    type: string;
    buffer: ArrayBuffer;
    name: string;
    size: number;
    lastModified: number;
    path: string;
    MD5?: string;
    preView?: boolean;
    savePath?: string;
    chunkData?: {
        offset: number;
        totalSize: number;
        buffer: ArrayBuffer;
    }
}
