import { v2 as cloudinary, UploadApiResponse } from 'cloudinary';
import { IStorageDriver } from '../interface/storage.interface';

interface CloudinaryConfig {
  cloudName: string;
  apiKey: string;
  apiSecret: string;
  folder?: string;
}

export class CloudinaryDriver implements IStorageDriver {
  constructor(private config: CloudinaryConfig) {
    cloudinary.config({
      cloud_name: config.cloudName,
      api_key: config.apiKey,
      api_secret: config.apiSecret,
      secure: true,
    });
  }

  /*
  ===============================
  Extract public_id from a Cloudinary URL
  e.g. https://res.cloudinary.com/<cloud>/image/upload/v123456/folder/name.jpg
       -> folder/name
  ===============================
  */
  private extractPublicIdFromUrl(fileUrl: string): {
    publicId: string;
    resourceType: string;
  } {
    const url = new URL(fileUrl);
    const parts = url.pathname.split('/').filter(Boolean);
    // parts: [<cloud_name>, <resource_type>, 'upload', 'v123456', ...publicIdParts]
    const resourceType = parts[1] || 'image';
    const uploadIndex = parts.indexOf('upload');
    let publicIdParts = parts.slice(uploadIndex + 1);

    // drop version segment (v123456) if present
    if (publicIdParts[0] && /^v\d+$/.test(publicIdParts[0])) {
      publicIdParts = publicIdParts.slice(1);
    }

    let publicId = decodeURIComponent(publicIdParts.join('/'));

    // raw files keep their extension in the public_id; image/video do not
    if (resourceType !== 'raw') {
      publicId = publicId.replace(/\.[^/.]+$/, '');
    }

    return { publicId, resourceType };
  }

  /*
  ===============================
  Upload
  Returns the full secure CDN URL of the uploaded asset
  ===============================
  */
  async upload(file: Buffer, key: string, mimeType?: string): Promise<string> {
    // strip extension: Cloudinary appends the format itself for image/video
    const publicId = key.replace(/\.[^/.]+$/, '');

    const result = await new Promise<UploadApiResponse>((resolve, reject) => {
      const stream = cloudinary.uploader.upload_stream(
        {
          public_id: publicId,
          folder: this.config.folder || undefined,
          resource_type: 'auto',
        },
        (error, res) => {
          if (error || !res) return reject(error);
          resolve(res);
        },
      );
      stream.end(file);
    });

    return result.secure_url;
  }

  /*
  ===============================
  Delete (accepts the stored Cloudinary URL)
  ===============================
  */
  async delete(fileUrl: string): Promise<void> {
    try {
      const { publicId, resourceType } = this.extractPublicIdFromUrl(fileUrl);
      const result = await cloudinary.uploader.destroy(publicId, {
        resource_type: resourceType,
      });
      if (result.result !== 'ok' && result.result !== 'not found') {
        throw new Error(`Cloudinary delete failed: ${result.result}`);
      }
      console.log(`Successfully deleted: ${publicId}`);
    } catch (err) {
      console.error('Cloudinary delete error:', err);
      throw err;
    }
  }

  /*
  ===============================
  URL
  upload() already returns a full https URL, so pass it through;
  otherwise treat the value as a public_id and build a URL
  ===============================
  */
  getUrl(pathOrUrl: string): string {
    if (pathOrUrl.startsWith('http')) return pathOrUrl;
    return cloudinary.url(pathOrUrl, { secure: true });
  }

  /*
  ===============================
  List images (Admin API)
  ===============================
  */
  async getAvailableImages(): Promise<{ Key?: string }[]> {
    try {
      const response = await cloudinary.api.resources({
        type: 'upload',
        resource_type: 'image',
        prefix: this.config.folder || undefined,
        max_results: 100,
      });
      const images = (response.resources || []).map(
        (r: { public_id: string; format?: string }) => ({
          Key: r.format ? `${r.public_id}.${r.format}` : r.public_id,
        }),
      );
      console.log(`Found ${images.length} images in Cloudinary`);
      return images;
    } catch (err) {
      console.error('Error listing Cloudinary resources:', err);
      throw err;
    }
  }
}
