import stream, { Readable } from 'stream';
import { ContainerGetPropertiesResponse } from '@azure/storage-blob';
import { HeadBucketCommandOutput } from '@aws-sdk/client-s3';
import { AwsCredentialIdentity } from '@aws-sdk/types';

type UploadResponse = {
    key: string;
};

type FileContent = Readable | ReadableStream | Blob | Buffer;

type ListRequestOptions = {
    prefix?: string;
    limit?: number;
    pagination?: string;
};

type ListResponseItem = {
    key: string;
    lastModified: Date;
    size: number;
    eTag: string;
};

type ListResponse = {
    items: ListResponseItem[];
    pagination?: string | null;
    count: number;
};

type GetObjectResponse = {
    contentType?: string;
    contentLength?: number;
    metadata?: {
        [key: string]: string;
    };
    lastModified?: Date;
    eTag?: string;
    contentEncoding?: string;
    cacheControl?: string;
    contentDisposition?: string;
    contentLanguage?: string;
    body?: Readable;
};

type GetUploadUrlResponse = {
    key: string;
    signedUrl: string;
};

type CreateUploadWriteStreamResponse = {
    key: string;
    stream: stream.PassThrough;
    promise: Promise<unknown>;
};

type HeadBucketResponse = HeadBucketCommandOutput | ContainerGetPropertiesResponse;

type ObjectStorageOptions = {
    region: string;
    credentials: AwsCredentialIdentity;
    provider: string;
};

type ListPartsMultipartUploadResponse = {
    PartNumberMarker?: number;
    NextPartNumberMarker?: number;
    MaxParts?: number;
    IsTruncated?: boolean;
    Parts?: {
        PartNumber: number;
        LastModified: Date;
        ETag: string;
        Size: number;
    }[];
    Initiator?: {
        ID?: string;
        DisplayName?: string;
    };
    Owner?: {
        DisplayName?: string;
        ID?: string;
    };
    StorageClass?: string;
    RequestCharged?: string;
};

type ListMultipartUploadsResponse = {
    bucketName: string;
    uploads: {
        uploadId: string;
        key: string;
        initiator?: string;
        owner?: string;
        storageClass?: string;
        initiated: Date | string;
    }[];
};

interface IObjectStorage {
    list(options: ListRequestOptions): Promise<ListResponse>;
    listAll(options: ListRequestOptions): Promise<ListResponse>;
    getHeadObject(key: string): Promise<GetObjectResponse>;
    getObject(key: string, options?: IGetObjectOptions): Promise<GetObjectResponse>;
    getDownloadUrl(key: string, expiresInMinutes: number): Promise<string>;
    getUploadUrl(key: string, expiresInMinutes: number): Promise<GetUploadUrlResponse>;
    upload(key: string, body: FileContent, metadata?: {
        [key: string]: string;
    }): Promise<UploadResponse>;
    createUploadWriteStream(key: string): CreateUploadWriteStreamResponse;
    delete(key: string): Promise<boolean>;
    getHeadBucket(): Promise<HeadBucketResponse>;
    generateUploadIdMultipart(key: string): Promise<string>;
    listMultipartUploadsForBucket(): Promise<ListMultipartUploadsResponse>;
    listMultipartUploadsForKey(key: string, uploadId?: string): Promise<ListPartsMultipartUploadResponse>;
    uploadMultipart(key: string, file: FileContent, partNumber: number, uploadId?: string): Promise<string>;
    completeMultipartUpload(key: string, uploadId: string): Promise<void>;
    abortMultipartUpload(key: string, uploadId: string): Promise<void>;
    getMultipartUploadPresignedUrl(key: string, uploadId: string, partNumber: string, expiresInMinutes?: number): Promise<string>;
}

interface IGetObjectOptions {
    range: string;
}

declare class ObjectStorageService {
    static bucketName: string;
    static objectStorageOptions: ObjectStorageOptions;
    constructor(bucketName: string, options?: ObjectStorageOptions);
    static getObjectStorageServiceInstance(bucketName?: string, options?: ObjectStorageOptions | undefined): Promise<IObjectStorage>;
    /**
     * Retrieves a list of objects from the object storage service.
     *
     * @param {ListRequestOptions} options - The options to apply to the list operation.
     * @param {string} [bucketName] - The name of the bucket to list objects from. If not provided, the default bucket name will be used.
     * @return {Promise<ListResponse>} A promise that resolves to the list of objects.
     */
    static list(options: ListRequestOptions, bucketName?: string): Promise<ListResponse>;
    /**
     * Retrieves a list of all objects from the object storage service.
     *
     * @param {ListRequestOptions} options - The options to apply to the list operation.
     * @param {string} [bucketName] - The name of the bucket to list objects from. If not provided, the default bucket name will be used.
     * @return {Promise<ListResponse>} A promise that resolves to the list of all objects.
     */
    static listAll(options: ListRequestOptions, bucketName?: string): Promise<ListResponse>;
    /**
     * Retrieves an object from the object storage service.
     *
     * @param {string} key - The key of the object to retrieve.
     * @param {string} [bucketName] - The name of the bucket where the object is stored. If not provided, the default bucket name will be used.
     * @return {Promise<GetObjectResponse>} A promise that resolves to the retrieved object.
     */
    static getObject(key: string, bucketName?: string, options?: IGetObjectOptions): Promise<GetObjectResponse>;
    /**
     * Retrieves an object info (without file content) from the object storage service.
     * @param key - The key of the object to retrieve.
     * @returns A promise that resolves to the info of the file.
     */
    static getHeadObject(key: string, bucketName?: string): Promise<GetObjectResponse>;
    /**
     * Retrieves a signed URL for the specified object in the object storage service.
     *
     * @param {string} key - The key of the object for which to generate the signed URL.
     * @param {number} expiresInMinutes - The number of minutes until the signed URL expires.
     * @param {string} [bucketName] - The name of the bucket where the object is stored. If not provided, the default bucket name will be used.
     * @return {Promise<string>} A promise that resolves to the generated signed URL.
     */
    static getDownloadUrl(key: string, expiresInMinutes: number, bucketName?: string): Promise<string>;
    /**
     * Retrieves an upload URL for the specified object in the object storage service.
     *
     * @param {string} key - The key of the object for which to generate the upload URL.
     * @param {number} expiresInMinutes - The number of minutes until the upload URL expires.
     * @param {string} [bucketName] - The name of the bucket where the object will be stored. If not provided, the default bucket name will be used.
     * @return {Promise<string>} A promise that resolves to the generated upload URL.
     */
    static getUploadUrl(key: string, expiresInMinutes: number, bucketName?: string): Promise<GetUploadUrlResponse>;
    /**
     * Uploads a file to the object storage service.
     *
     * @param {string} key - The key of the object to upload.
     * @param {FileContent} body - The content of the file to upload.
     * @param {Object} [metadata] - Optional metadata to associate with the object.
     * @param {string} [bucketName] - The name of the bucket where the object will be stored. If not provided, the default bucket name will be used.
     * @return {Promise<UploadResponse>} A promise that resolves to the response of the upload operation.
     */
    static upload(key: string, body: FileContent, metadata?: {
        [key: string]: string;
    }, bucketName?: string): Promise<UploadResponse>;
    /**
     * Creates an upload write stream for the specified key in the object storage service.
     *
     * @param {string} key - The key of the object to create the upload write stream for.
     * @param {string} [bucketName] - The name of the bucket where the object will be stored. If not provided, the default bucket name will be used.
     * @return {Promise<CreateUploadWriteStreamResponse>} A promise that resolves to the response of the upload operation.
     */
    static createUploadWriteStream(key: string, bucketName?: string): Promise<CreateUploadWriteStreamResponse>;
    /**
     * Deletes an object or multiple objects from the object storage service.
     *
     * @param {string | string[]} key - The key or array of keys of the objects to delete.
     * @param {string} [bucketName] - The name of the bucket where the objects are stored. If not provided, the default bucket name will be used.
     * @return {Promise<{ key: string; deleted: boolean; error?: string }[] | boolean>} A promise that resolves to an array of objects containing the key, deleted status, and an optional error message for each object deleted, or a boolean value indicating the success of the deletion.
     */
    static delete(key: string | string[], bucketName?: string): Promise<{
        key: string;
        deleted: boolean;
        error?: string;
    }[] | boolean>;
    /**
     * Retrieves the head bucket from the object storage service.
     *
     * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
     * @return {Promise<HeadBucketResponse>} A promise that resolves to the head bucket response.
     */
    static getHeadBucket(bucketName?: string): Promise<HeadBucketResponse>;
    /**
     * Generates a unique upload ID for a multipart upload.
     *
     * @param {string} key - The key of the object to upload.
     * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
     * @return {Promise<string>} A promise that resolves to the generated upload ID.
     */
    static generateUploadIdMultipart(key: string, bucketName?: string): Promise<string>;
    /**
     * Retrieves a list of multipart uploads for a specified key in the object storage service.
     *
     * @param {string} key - The key of the object to retrieve uploads for.
     * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
     * @param {string} [uploadId] - The upload ID to filter the results by.
     * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of multipart uploads for the specified key.
     */
    static listMultipartUploadsForKey(key: string, bucketName?: string, uploadId?: string): Promise<ListPartsMultipartUploadResponse>;
    /**
     * Retrieves a list of all multipart uploads for a specified bucket in the object storage service.
     *
     * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
     * @return {Promise<ListMultipartUploadsResponse>} A promise that resolves to the list of multipart uploads for the specified bucket.
     */
    static listMultipartUploadsForBucket(bucketName?: string): Promise<ListMultipartUploadsResponse>;
    /**
     * Uploads a multipart file to the specified bucket in the object storage service.
     *
     * @param {string} key - The key of the object to upload the multipart file to.
     * @param {FileContent} file - The content of the file to upload.
     * @param {number} partNumber - The number of the part being uploaded.
     * @param {string} [uploadId] - The ID of the multipart upload.
     * @param {string} [bucketName] - The name of the bucket to upload the file to. If not provided, the default bucket name will be used.
     * @return {Promise<string>} A Promise that resolves to the base64 encoded block ID of the uploaded part.
     */
    static uploadMultipart(key: string, file: FileContent, partNumber: number, uploadId?: string, bucketName?: string): Promise<string>;
    /**
     * Completes a multipart upload for the specified object in the object storage service.
     *
     * @param {string} key - The key of the object to complete the multipart upload for.
     * @param {string} uploadId - The ID of the multipart upload to complete.
     * @param {string} [bucketName] - The name of the bucket to complete the multipart upload in. If not provided, the default bucket name will be used.
     * @return {Promise<void>} A Promise that resolves when the multipart upload is completed.
     */
    static completeMultipartUpload(key: string, uploadId: string, bucketName?: string): Promise<void>;
    /**
     * Aborts a multipart upload for the specified object in the object storage service.
     *
     * @param {string} key - The key of the object to abort the multipart upload for.
     * @param {string} uploadId - The ID of the multipart upload to abort.
     * @param {string} [bucketName] - The name of the bucket to abort the multipart upload in. If not provided, the default bucket name will be used.
     * @return {Promise<void>} A Promise that resolves when the multipart upload is aborted.
     */
    static abortMultipartUpload(key: string, uploadId: string, bucketName?: string): Promise<void>;
    /**
     * Retrieves a presigned URL for a specific part of a multipart upload.
     *
     * @param {string} key - The key of the object for which to generate the presigned URL.
     * @param {string} uploadId - The ID of the multipart upload.
     * @param {string} partNumber - The number of the part for which to generate the presigned URL.
     * @param {number} [expiresInMinutes] - The number of minutes until the presigned URL expires. Default is 60 minutes.
     * @return {Promise<string>} A Promise that resolves to the presigned URL for the specified part of the multipart upload.
     */
    static getMultipartUploadPresignedUrl(key: string, uploadId: string, partNumber: string, expiresInMinutes?: number): Promise<string>;
}

export { FileContent, GetObjectResponse, GetUploadUrlResponse, ListRequestOptions, ListResponse, ListResponseItem, ObjectStorageService, UploadResponse };
