/**
 * ProjFS-FUSE.ONE - FUSE3-compatible API for Windows using ProjFS
 * 
 * This package provides a FUSE3-compatible interface on Windows by using
 * Windows Projected File System (ProjFS) as the backend. Applications can
 * use the same FUSE3 API on both Linux and Windows for cross-platform
 * virtual filesystem development.
 * 
 * @author REFINIO GmbH
 * @license MIT
 */

import { EventEmitter } from 'events';
import { createRequire } from 'module';

const require = createRequire(import.meta.url);

// FUSE3-compatible operation interfaces
export interface FuseStats {
    mtime: Date;
    atime: Date;
    ctime: Date;
    size: number;
    mode: number;
    uid: number;
    gid: number;
}

export interface FuseOperations {
    init?: () => void;
    getattr?: (path: string) => FuseStats | null;
    readdir?: (path: string) => string[];
    read?: (path: string, size: number, offset: number) => Buffer | null;
    write?: (path: string, buffer: Buffer, offset: number) => number;
    create?: (path: string, mode: number) => void;
    unlink?: (path: string) => void;
    mkdir?: (path: string, mode: number) => void;
    rmdir?: (path: string) => void;
    rename?: (oldPath: string, newPath: string) => void;
    truncate?: (path: string, size: number) => void;
    open?: (path: string, flags: number) => number;
    release?: (path: string, fd: number) => void;
    statfs?: (path: string) => any;
}

// Load ProjFS-FUSE bridge
let projfsFuse: any;
try {
    projfsFuse = require('bindings')('projfs_fuse');
} catch (error: any) {
    throw new Error(`Failed to load ProjFS-FUSE bridge: ${error.message}`);
}

/**
 * ProjFS-FUSE filesystem class
 * 
 * Provides a FUSE3-compatible interface on Windows using ProjFS as backend.
 * This allows the same FUSE3 code to work on both Linux and Windows.
 */
export class ProjFSFuse extends EventEmitter {
    private mounted = false;
    private mountPath: string;
    private operations: FuseOperations;
    private nativeInstance: any;

    constructor(mountPath: string, operations: FuseOperations, options: any = {}) {
        super();
        
        if (process.platform !== 'win32') {
            throw new Error('ProjFS-FUSE.ONE only works on Windows. For Linux, use fuse3.one');
        }

        this.mountPath = mountPath;
        this.operations = operations;
        
        // Create ProjFS mount instance
        this.nativeInstance = new projfsFuse.ProjFSMount(mountPath);
    }

    /**
     * Mount the filesystem with FUSE3-compatible operations
     * 
     * This method adapts FUSE3 operations to ProjFS callbacks automatically
     */
    async mount(): Promise<void> {
        if (this.mounted) {
            throw new Error('Filesystem is already mounted');
        }

        try {
            // Convert FUSE3 operations to ProjFS operations
            const projfsOperations = this.adaptFuseOperationsToProjFS(this.operations);
            
            await this.nativeInstance.mount(projfsOperations);
            this.mounted = true;
            this.emit('mount');
        } catch (error: any) {
            throw new Error(`Failed to mount ProjFS-FUSE filesystem: ${error.message}`);
        }
    }

    /**
     * Unmount the filesystem
     */
    async unmount(): Promise<void> {
        if (!this.mounted) {
            return;
        }

        try {
            await this.nativeInstance.unmount();
            this.mounted = false;
            this.emit('unmount');
        } catch (error: any) {
            throw new Error(`Failed to unmount ProjFS-FUSE filesystem: ${error.message}`);
        }
    }

    /**
     * Check if filesystem is mounted
     */
    isMounted(): boolean {
        return this.mounted && this.nativeInstance.isMounted();
    }

    /**
     * Get mount path
     */
    getMountPath(): string {
        return this.mountPath;
    }

    /**
     * Adapt FUSE3 operations to ProjFS callbacks
     * 
     * This is the core adapter logic that makes FUSE3 operations work with ProjFS
     */
    private adaptFuseOperationsToProjFS(fuseOps: FuseOperations): any {
        return {
            // ProjFS readdir maps directly to FUSE readdir
            readdir: (path: string): string[] => {
                if (fuseOps.readdir) {
                    return fuseOps.readdir(path);
                }
                return [];
            },
            
            // Future: Add more operation mappings
            // getattr -> getPlaceholderInfo
            // read -> getFileData
            // etc.
        };
    }
}

// Export default class for compatibility
export default ProjFSFuse;

// Export FUSE3-compatible error codes
export const FUSE_ERRORS = {
    EPERM: 1,
    ENOENT: 2,
    EIO: 5,
    EACCES: 13,
    EEXIST: 17,
    ENOTDIR: 20,
    EISDIR: 21,
    EINVAL: 22,
    ENOSPC: 28,
    EROFS: 30,
    EBUSY: 16,
    ENOTEMPTY: 39
};

// Export for backwards compatibility with existing FUSE3 code
export { ProjFSFuse as Fuse3 };