import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as Minio from 'minio';
import * as fs from 'fs';

@Injectable()
export class MinioStorageAdapter implements OnModuleInit {
  private readonly logger = new Logger(MinioStorageAdapter.name);
  private readonly minioClient: Minio.Client;
  private readonly bucketName: string;

  constructor(private readonly configService: ConfigService) {
    const endpoint = this.configService.get<string>('MINIO_ENDPOINT', 'localhost');
    const port = parseInt(this.configService.get<string>('MINIO_PORT', '9000'), 10);
    const useSSL = this.configService.get<string>('MINIO_USE_SSL', 'false') === 'true';
    const accessKey = this.configService.get<string>('MINIO_ACCESS_KEY', 'admin');
    const secretKey = this.configService.get<string>('MINIO_SECRET_KEY', '');

    this.bucketName = this.configService.get<string>('MINIO_BACKUP_BUCKET', 'snapshots');

    this.minioClient = new Minio.Client({
      endPoint: endpoint,
      port,
      useSSL,
      accessKey,
      secretKey,
    });

    this.logger.log(`MinIO storage adapter configured: ${endpoint}:${port}, bucket: ${this.bucketName}`);
  }

  async onModuleInit() {
    await this.ensureBucketExists();
  }

  private async ensureBucketExists(): Promise<void> {
    try {
      const exists = await this.minioClient.bucketExists(this.bucketName);
      if (!exists) {
        await this.minioClient.makeBucket(this.bucketName, 'cn-east-1');
        this.logger.log(`Created backup bucket: ${this.bucketName}`);
      } else {
        this.logger.log(`Backup bucket exists: ${this.bucketName}`);
      }
    } catch (error) {
      this.logger.error(`Failed to ensure backup bucket exists: ${error.message}`);
    }
  }

  async upload(localFilePath: string, objectPath: string): Promise<string> {
    const stat = fs.statSync(localFilePath);

    await this.minioClient.fPutObject(
      this.bucketName,
      objectPath,
      localFilePath,
      { 'Content-Type': 'application/gzip' },
    );

    this.logger.log(`Uploaded to MinIO: ${this.bucketName}/${objectPath}, size: ${stat.size} bytes`);
    return `${this.bucketName}/${objectPath}`;
  }

  async delete(objectPath: string): Promise<void> {
    try {
      await this.minioClient.removeObject(this.bucketName, objectPath);
      this.logger.log(`Deleted from MinIO: ${this.bucketName}/${objectPath}`);
    } catch (error) {
      this.logger.error(`Failed to delete from MinIO: ${error.message}`);
    }
  }

  async deleteByPrefix(prefix: string): Promise<void> {
    const objects: string[] = [];
    const stream = this.minioClient.listObjects(this.bucketName, prefix, true);

    await new Promise<void>((resolve, reject) => {
      stream.on('data', (obj) => { if (obj.name) objects.push(obj.name); });
      stream.on('end', resolve);
      stream.on('error', reject);
    });

    if (objects.length > 0) {
      await this.minioClient.removeObjects(this.bucketName, objects);
      this.logger.log(`Deleted ${objects.length} objects from MinIO with prefix: ${prefix}`);
    }
  }
}
