import S3mini from '../S3.js';
import { S3Client } from 'bun';
import type { ExistResponseCode } from '../types.js';

export class S3miniBun extends S3mini {
  private nativeBunS3Client: S3Client;
  constructor(config: ConstructorParameters<typeof S3mini>[0]) {
    super(config);

    if (!process.versions.bun) {
      throw new Error('s3miniBun cannot be used outside of the bun runtime!');
    }

    // TODO: Acutally implement functionality behind these:
    if (config.requestSizeInBytes) {
      console.warn('requestSizeInBytes not yet implemented for bun native client');
    }
    if (config.requestAbortTimeout) {
      console.warn('requestAbortTimeout not yet implemented for bun native client');
    }
    if (config.logger) {
      console.warn('logger not yet implemented for bun native client');
    }

    this.nativeBunS3Client = new S3Client({
      accessKeyId: config.accessKeyId,
      secretAccessKey: config.secretAccessKey,
      region: config.region,
      endpoint: config.endpoint,
    });
  }

  public override async deleteObject(key: string): Promise<boolean> {
    await this.nativeBunS3Client.delete(key);
    //TODO investigate on how buns errors diverge from default implementation
    return true;
  }

  public override deleteObjects(key: string[]): Promise<boolean[]> {
    return Promise.all(key.map(k => this.deleteObject(k)));
  }

  public override objectExists(key: string, opts?: Record<string, unknown>): Promise<ExistResponseCode> {
    return this.nativeBunS3Client.exists(key, opts);
  }

  public override getObject(key: string, opts?: Record<string, unknown>): Promise<string | null> {
    return this.nativeBunS3Client.file(key, opts).text();
  }

  public override getObjectArrayBuffer(key: string, opts?: Record<string, unknown>): Promise<ArrayBuffer | null> {
    return this.nativeBunS3Client.file(key, opts).arrayBuffer();
  }

  public override getObjectJSON<T = unknown>(key: string, opts?: Record<string, unknown>): Promise<T | null> {
    return this.nativeBunS3Client.file(key, opts).json();
  }

  public override async listObjects(
    delimiter?: string,
    prefix?: string,
    maxKeys?: number,
    opts?: Record<string, unknown>,
  ) {
    return (
      (
        await this.nativeBunS3Client.list({
          delimiter,
          prefix,
          maxKeys,
          ...opts,
        })
      ).contents ?? null
    );
  }

  public override putObject(key: string, data: string | Buffer, fileType?: string): Promise<Response | number> {
    return this.nativeBunS3Client.file(key, { type: fileType }).write(data);
  }

  public override getContentLength(key: string): Promise<number> {
    return this.nativeBunS3Client.size(key);
  }

  public override async getEtag(key: string, opts?: Record<string, unknown>): Promise<string | null> {
    return (await this.nativeBunS3Client.stat(key, opts)).etag;
  }
}
