import { Readable } from "stream";
import {
  S3Client,
  PutObjectCommand,
  GetObjectCommand,
  DeleteObjectCommand,
  PutObjectCommandOutput,
  DeleteObjectCommandOutput,
  GetObjectCommandOutput,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3Client = new S3Client({ region: process.env.AWS_REGION });

/**
 * Sube un objeto a S3.
 */
export async function uploadObject(
  bucket: string,
  key: string,
  body: Buffer | string,
  options?: Omit<PutObjectCommand["input"], "Bucket" | "Key" | "Body">
): Promise<PutObjectCommandOutput> {
  const command = new PutObjectCommand({
    Bucket: bucket,
    Key: key,
    Body: body,
    ...options,
  });
  return s3Client.send(command);
}

/**
 * Obtiene un objeto de S3 como Buffer.
 */
export async function getObject(bucket: string, key: string): Promise<Buffer> {
  const command = new GetObjectCommand({ Bucket: bucket, Key: key });
  const response: GetObjectCommandOutput = await s3Client.send(command);
  const stream = response.Body as Readable;
  return streamToBuffer(stream);
}

async function streamToBuffer(stream: Readable): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    const chunks: Uint8Array[] = [];
    stream.on("data", (chunk) => chunks.push(chunk as Uint8Array));
    stream.on("error", reject);
    stream.on("end", () => resolve(Buffer.concat(chunks)));
  });
}

/**
 * Elimina un objeto de S3.
 */
export async function deleteObject(
  bucket: string,
  key: string
): Promise<DeleteObjectCommandOutput> {
  const command = new DeleteObjectCommand({ Bucket: bucket, Key: key });
  return s3Client.send(command);
}

/**
 * Genera un URL firmado para descargar (GET) un objeto privado.
 * @param expiresIn Segundos de validez del URL (por defecto 900 = 15 minutos).
 */
export async function getSignedGetUrl(
  bucket: string,
  key: string,
  expiresIn: number = 900
): Promise<string> {
  const command = new GetObjectCommand({ Bucket: bucket, Key: key });
  return getSignedUrl(s3Client, command, { expiresIn });
}

/**
 * Genera un URL firmado para subir (PUT) un objeto privado.
 * @param expiresIn Segundos de validez del URL (por defecto 900 = 15 minutos).
 */
export async function getSignedPutUrl(
  bucket: string,
  key: string,
  expiresIn: number = 900
): Promise<string> {
  const command = new PutObjectCommand({ Bucket: bucket, Key: key });
  return getSignedUrl(s3Client, command, { expiresIn });
}
