import {
  DeleteObjectCommand,
  HeadObjectCommand,
  S3Client,
} from '@aws-sdk/client-s3';
import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

import type { StorageConfig } from './config';

export function createS3Client(config: StorageConfig): S3Client {
  return new S3Client({
    region: config.S3_REGION,
    endpoint: config.S3_ENDPOINT,
    forcePathStyle: config.S3_FORCE_PATH_STYLE,
    credentials: {
      accessKeyId: config.S3_ACCESS_KEY,
      secretAccessKey: config.S3_SECRET_KEY,
    },
  });
}

export function backupObjectKey(serverId: string, backupId: string): string {
  return `servers/${serverId}/backups/${backupId}.tar.gz`;
}

export function worldUploadObjectKey(serverId: string, uploadId: string): string {
  return `servers/${serverId}/uploads/${uploadId}/world.tar.gz`;
}

export async function createPresignedPutUrl(
  client: S3Client,
  bucket: string,
  key: string,
  expiresInSeconds: number,
  contentType = 'application/gzip',
): Promise<string> {
  const command = new PutObjectCommand({
    Bucket: bucket,
    Key: key,
    ContentType: contentType,
  });

  return getSignedUrl(client, command, { expiresIn: expiresInSeconds });
}

export async function createPresignedGetUrl(
  client: S3Client,
  bucket: string,
  key: string,
  expiresInSeconds: number,
): Promise<string> {
  const command = new GetObjectCommand({
    Bucket: bucket,
    Key: key,
  });

  return getSignedUrl(client, command, { expiresIn: expiresInSeconds });
}

export async function deleteStoredObject(
  client: S3Client,
  bucket: string,
  key: string,
): Promise<void> {
  await client.send(
    new DeleteObjectCommand({
      Bucket: bucket,
      Key: key,
    }),
  );
}

export async function headStoredObject(
  client: S3Client,
  bucket: string,
  key: string,
): Promise<{ sizeBytes: number; etag?: string }> {
  const response = await client.send(
    new HeadObjectCommand({
      Bucket: bucket,
      Key: key,
    }),
  );

  return {
    sizeBytes: Number(response.ContentLength ?? 0),
    etag: response.ETag,
  };
}
