import type { SmartArchive } from './classes.smartarchive.js';
import * as plugins from './plugins.js';

class DecompressZipTransform extends plugins.smartstream.SmartDuplex<ArrayBufferLike> {
  private streamtools: plugins.smartstream.IStreamTools;
  private unzipper = new plugins.fflate.Unzip(async (fileArg) => {
    let resultBuffer: Buffer;
    fileArg.ondata = async (flateError, dat, final) => {
      resultBuffer? resultBuffer = Buffer.concat([resultBuffer, Buffer.from(dat)])
        : resultBuffer = Buffer.from(dat);
      if (final) {
        const streamFile = plugins.smartfile.StreamFile.fromBuffer(resultBuffer);
        streamFile.relativeFilePath = fileArg.name;
        this.streamtools.push(streamFile);
      }
    }
    fileArg.start();
  });
  constructor() {
    super({
      objectMode: true,
      writeFunction: async (chunkArg: Buffer, streamtoolsArg) => {
        this.streamtools? null : this.streamtools = streamtoolsArg;
        this.unzipper.push(chunkArg, false);
      },
      finalFunction: async () => {
        this.unzipper.push(Buffer.from(''), true);
        await plugins.smartdelay.delayFor(0);
        await this.streamtools.push(null);
      }
    });
    this.unzipper.register(plugins.fflate.UnzipInflate);
  }
}

// This class wraps fflate's zip in a Node.js Transform stream for compression
export class CompressZipTransform extends plugins.stream.Transform {
  files: { [fileName: string]: Uint8Array };

  constructor() {
    super();
    this.files = {};
  }

  _transform(chunk: Buffer, encoding: BufferEncoding, callback: plugins.stream.TransformCallback) {
    // Simple example: storing chunks in memory before finalizing ZIP in _flush
    this.files['file.txt'] = new Uint8Array(chunk);
    callback();
  }

  _flush(callback: plugins.stream.TransformCallback) {
    plugins.fflate.zip(this.files, (err, zipped) => {
      if (err) {
        callback(err);
      } else {
        this.push(Buffer.from(zipped));
        callback();
      }
    });
  }
}

export class ZipTools {
  constructor() {
  }

  public getCompressionStream() {
    return new CompressZipTransform();
  }

  public getDecompressionStream() {
    return new DecompressZipTransform();
  }
}