import { Injectable, Logger } from '@nestjs/common';
import * as crypto from 'crypto';
import { Client as MinioClient } from 'minio';

const BUCKET = 'app-releases';

@Injectable()
export class FileStorageService {
  private readonly logger = new Logger(FileStorageService.name);
  private readonly minio: MinioClient;

  constructor() {
    this.minio = new MinioClient({
      endPoint: process.env.MINIO_ENDPOINT || 'localhost',
      port: parseInt(process.env.MINIO_PORT || '9000', 10),
      useSSL: process.env.MINIO_USE_SSL === 'true',
      accessKey: process.env.MINIO_ACCESS_KEY || 'minioadmin',
      secretKey: process.env.MINIO_SECRET_KEY || 'minioadmin',
    });
  }

  /** Upload file to MinIO and return metadata */
  async uploadFile(
    buffer: Buffer,
    originalName: string,
    platform: string,
    versionName: string,
  ) {
    // Ensure bucket exists
    const exists = await this.minio.bucketExists(BUCKET);
    if (!exists) {
      await this.minio.makeBucket(BUCKET);
    }

    // Compute SHA256
    const sha256 = crypto.createHash('sha256').update(buffer).digest('hex');

    // Generate object name
    const ext = originalName.split('.').pop() || 'bin';
    const timestamp = Date.now();
    const random = crypto.randomBytes(4).toString('hex');
    const objectName = `${platform}/${versionName}/${timestamp}-${random}.${ext}`;

    // Upload
    await this.minio.putObject(BUCKET, objectName, buffer, buffer.length, {
      'Content-Type':
        ext === 'apk'
          ? 'application/vnd.android.package-archive'
          : 'application/octet-stream',
    });

    return {
      objectName,
      fileSize: buffer.length.toString(),
      sha256,
    };
  }

  /** Stream file from MinIO to a writable (e.g. Express Response) */
  async streamFile(objectName: string, destination: NodeJS.WritableStream): Promise<void> {
    const stream = await this.minio.getObject(BUCKET, objectName);
    await new Promise<void>((resolve, reject) => {
      stream.pipe(destination as any);
      stream.on('end', resolve);
      stream.on('error', reject);
    });
  }

  /** Get file metadata (size) from MinIO */
  async statFile(objectName: string): Promise<{ size: number; contentType: string }> {
    const stat = await this.minio.statObject(BUCKET, objectName);
    return {
      size: stat.size,
      contentType: (stat.metaData as any)?.['content-type'] || 'application/octet-stream',
    };
  }
}
