import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import yenv from "yenv";
import { v4 as uuidv4 } from "uuid";

const env = yenv("env.yaml", { env: "development" });

export default class S3Service {
  private static instance: S3Service;
  private s3Client!: S3Client;
  private readonly bucketName: string;
  private readonly region: string;

  private constructor() {
    this.bucketName = env.AWS_S3_BUCKET_NAME;
    this.region = env.AWS_REGION;
    this.initializeS3Client();
  }

  static getInstance() {
    if (!S3Service.instance) {
      S3Service.instance = new S3Service();
    }
    return S3Service.instance;
  }

  private initializeS3Client() {
    this.s3Client = new S3Client({
      region: this.region,
      credentials: {
        accessKeyId: env.AWS_ACCESS_KEY_ID,
        secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
      },
    });
  }

  /**
   * Upload a file to S3
   * @param fileBuffer - The file buffer to upload
   * @param fileName - Original file name
   * @param mimeType - File MIME type
   * @param folder - Optional folder path in S3 bucket
   * @returns Object containing file URL and key
   */
  async uploadFile(
    fileBuffer: Buffer,
    fileName: string,
    mimeType: string,
    folder: string = "uploads"
  ): Promise<{ url: string; key: string }> {
    try {
      // Generate unique file name
      const fileExtension = fileName.split(".").pop();
      const uniqueFileName = `${Date.now()}-${uuidv4()}.${fileExtension}`;
      const key = `${folder}/${uniqueFileName}`;

      const command = new PutObjectCommand({
        Bucket: this.bucketName,
        Key: key,
        Body: fileBuffer,
        ContentType: mimeType,
      });

      await this.s3Client.send(command);

      // Generate public URL
      const url = `https://${this.bucketName}.s3.${this.region}.amazonaws.com/${key}`;

      return { url, key };
    } catch (error) {
      throw new Error(`Failed to upload file to S3: ${error}`);
    }
  }

  /**
   * Get file URL from S3 key
   * @param key - S3 object key
   * @returns File URL
   */
  getFileUrl(key: string): string {
    return `https://${this.bucketName}.s3.${this.region}.amazonaws.com/${key}`;
  }

  /**
   * Delete a file from S3
   * @param key - S3 object key
   * @returns Success status
   */
  async deleteFile(key: string): Promise<boolean> {
    try {
      const command = new DeleteObjectCommand({
        Bucket: this.bucketName,
        Key: key,
      });

      await this.s3Client.send(command);
      return true;
    } catch (error) {
      throw new Error(`Failed to delete file from S3: ${error}`);
    }
  }

  /**
   * Get S3 client instance
   * @returns S3Client instance
   */
  getClient(): S3Client {
    return this.s3Client;
  }

  /**
   * Get bucket name
   * @returns Bucket name
   */
  getBucketName(): string {
    return this.bucketName;
  }
}
