import { uuidV4 } from "@api-client/core/build/browser.js";
import { 
  FileBindings, 
  FileReadOptions, 
  FileWriteOptions, 
  IOpenFileDialogInit, 
  IOpenFileDialogResult, 
  ISaveFileDialogInit, 
  ISaveFileDialogResult 
} from "../base/FileBindings.js";

export class WebFileBindings extends FileBindings {
  /**
   * Keeps references to file handles mapped to the generated path.
   */
  protected handles = new Map<string, FileSystemFileHandle>();

  async saveFileDialog(opts: ISaveFileDialogInit = {}): Promise<ISaveFileDialogResult> {
    const { excludeAcceptAllOption, filters } = opts;
    const dialogOptions: SaveFilePickerOptions = {
      excludeAcceptAllOption,      
    };
    if (Array.isArray(filters) && filters.length) {
      dialogOptions.types = filters;
    }
    try {
      const handle = await window.showSaveFilePicker(dialogOptions);
      const path = uuidV4();
      this.handles.set(path, handle);
      return { canceled: false, filePath: path };
    } catch (_) {
      return { canceled: true };
    }
  }

  async openFileDialog(opts: IOpenFileDialogInit = {}): Promise<IOpenFileDialogResult> {
    const { excludeAcceptAllOption, filters, multiple } = opts;
    const dialogOptions: OpenFilePickerOptions = {
      excludeAcceptAllOption,
      multiple,
    }
    if (Array.isArray(filters) && filters.length) {
      dialogOptions.types = filters;
    }
    try {
      const handles = await window.showOpenFilePicker(dialogOptions);
      const result: string[] = [];
      handles.forEach((handle) => {
        const path = `${uuidV4()}/${handle.name}`;
        result.push(path);
        this.handles.set(path, handle);
      });
      return { canceled: false, filePath: result };
    } catch (_) {
      return { canceled: true };
    }
  }

  async writeFile(path: string, contents: string | Buffer | BufferSource | Blob, opts: FileWriteOptions = {}): Promise<void> {
    const handle = this.handles.get(path);
    if (!handle) {
      throw new Error(`Invalid file path. The file handle not found.`);
    }
    const stream = await handle.createWritable();
    await stream.write(contents);
    await stream.close();
    if (opts.dispose) {
      this.dispose(path);
    }
  }

  async readFile(path: string, opts: FileReadOptions  = {}): Promise<string | Buffer | ArrayBuffer> {
    const handle = this.handles.get(path);
    if (!handle) {
      throw new Error(`Invalid file path. The file handle not found.`);
    }
    const file = await handle.getFile();
    let result: ArrayBuffer | string;
    if (opts.returnType === 'buffer') {
      result = await file.arrayBuffer();
    } else {
      result = await file.text();
    }
    if (opts.dispose) {
      this.dispose(path);
    }
    return result;
  }

  async dispose(path: string): Promise<void> {
    this.handles.delete(path);
  }
}
