import { CopyObjectCommand, DeleteObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { Injectable, Logger } from '@nestjs/common';
import { randomUUID } from 'crypto';
import sharp from 'sharp';
import { envs } from 'src/config';

/**
 * Objetos de imágenes sugeridas en Spaces. Misma optimización (WebP 1200 px) y misma
 * ruta final (`productos/<id>/<uuid>.webp`) que la carga manual de ProductosService,
 * así una imagen aprobada es indistinguible de una subida a mano.
 */
@Injectable()
export class StorageSugeridasService {
  private readonly logger = new Logger(StorageSugeridasService.name);
  private readonly s3 = new S3Client({
    region: envs.doSpacesRegion,
    endpoint: envs.doSpacesEndpoint || undefined,
    credentials: envs.doSpacesKey && envs.doSpacesSecret ? { accessKeyId: envs.doSpacesKey, secretAccessKey: envs.doSpacesSecret } : undefined,
    forcePathStyle: false,
  });

  private get bucket(): string {
    if (!envs.doSpacesBucket) throw new Error('DO_SPACES_BUCKET no está configurado');
    return envs.doSpacesBucket;
  }

  urlPublica(path: string): string {
    const base = (envs.doSpacesPublicUrl || envs.doSpacesEndpoint || `https://${this.bucket}.${envs.doSpacesRegion}.digitaloceanspaces.com`).replace(/\/+$/, '');
    return `${base}/${path}`;
  }

  async subirSugerida(empresaId: string, productoId: string, original: Buffer): Promise<{ path: string; tamanhoKb: number }> {
    // flatten: los PNG con transparencia quedan sobre blanco, como pide la tienda.
    const webp = await sharp(original).rotate().resize({ width: 1200, withoutEnlargement: true }).flatten({ background: '#ffffff' }).webp({ quality: 80 }).toBuffer();
    const path = `sugeridas/${empresaId}/${productoId}/${randomUUID()}.webp`;
    await this.s3.send(new PutObjectCommand({ Bucket: this.bucket, Key: path, Body: webp, ContentType: 'image/webp', ACL: 'public-read' }));
    return { path, tamanhoKb: Math.round(webp.length / 1024) };
  }

  /** Copia la sugerida a la ruta de imágenes del producto. Lanza (NotFound) si el origen ya no existe. */
  async promoverAProducto(pathSugerida: string, productoId: string): Promise<{ path: string; tamanhoKb: number }> {
    const head = await this.s3.send(new HeadObjectCommand({ Bucket: this.bucket, Key: pathSugerida }));
    const path = `productos/${productoId}/${randomUUID()}.webp`;
    await this.s3.send(
      new CopyObjectCommand({
        Bucket: this.bucket,
        CopySource: `${this.bucket}/${pathSugerida}`,
        Key: path,
        ACL: 'public-read',
        ContentType: 'image/webp',
        MetadataDirective: 'REPLACE',
      }),
    );
    return { path, tamanhoKb: Math.round((head.ContentLength ?? 0) / 1024) };
  }

  async borrar(path: string): Promise<void> {
    try {
      await this.s3.send(new DeleteObjectCommand({ Bucket: this.bucket, Key: path }));
    } catch (e) {
      // Un objeto que ya no está no debe frenar el rechazo/limpieza.
      this.logger.warn(`No se pudo borrar ${path}: ${(e as Error).message}`);
    }
  }
}
