import { Injectable } from '@angular/core';
import {
  getStorage,
  ref,
  uploadBytesResumable,
  getDownloadURL,
  deleteObject,
} from 'firebase/storage';
import {
  getFirestore,
  doc,
  setDoc,
  getDoc,
  deleteDoc,
  updateDoc,
} from 'firebase/firestore';
import { NotifierService } from '../../../shared/notifierService/notifier.service';

@Injectable({
  providedIn: 'root',
})
export class ProfilePhotoUploadService {
  storage = getStorage();
  firestore = getFirestore();

  constructor(private notifierService: NotifierService) {}

  async uploadFile(userId: string, file: File, metadata: any): Promise<void> {
    // get the current user's file metadata from firestore
    const userDocRef = doc(this.firestore, 'users', userId);
    const fileDocRef = doc(this.firestore, 'files', userId);
    const fileDoc = await getDoc(fileDocRef);

    if (fileDoc.exists()) {
      // if there's an existing file, delete it from storage
      const existingFilePath = fileDoc.data()['filePath'];
      const existingFileRef = ref(this.storage, existingFilePath);
      await deleteObject(existingFileRef).catch((error) => {
        (`Error deleting existing file: ${{error}}`);
      });
    }

    if (file.size > 30 * 1024) {
      // If the file is too large, compress the image
      this.notifierService.showInformation('File size exceeds 30 KB. Compressing the image...')
      file = await this.compressImage(file);
    }

    //creates a firebase storage for images
    const filePath = `images/${userId}/${file.name}`;
    const storageRef = ref(this.storage, filePath);

    //uploads the image to the storage
    const uploadTask = uploadBytesResumable(storageRef, file, metadata);
    uploadTask.on(
      'state_changed',
      //show the upload process in the console
      (snapshot) => {
        switch (snapshot.state) {
          case 'error':
            this.notifierService.showInformation('Upload failed')
            break;
          case 'success':
            this.notifierService.showInformation('Upload completed')
            break;
        }
      },
      //error handling, display currently only in the console
      (error) => {
        switch (error.code) {
          case 'storage/unauthorized':
            this.notifierService.showInformation('The user is not authenticated. Please authenticate yourself and try again.');
            break;
          case 'storage/canceled':
            this.notifierService.showInformation('The user has cancelled the process.');
            break;
          case 'storage/unknown':
            this.notifierService.showInformation('An unknown error has occurred.');
            break;
        }
      },
      () => {
        //returns the download url of the uploaded image
        getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
          const fileMetadata = {
            id: userId,
            filePath: filePath,
            downloadURL: downloadURL,
          };
          // Set the file metadata in the 'files' collection
          setDoc(fileDocRef, fileMetadata).catch((error) => {
            this.notifierService.showInformation(`Error updating file${{error}}`);
          });
          // Update the user's document with the new photoUrl
          updateDoc(userDocRef, { photoUrl: downloadURL })
            .catch((error) => {
              this.notifierService.showInformation(`Error updating user document with photo URL: ${{error}}`);
            });
        });
      }
    );
  }

  async deleteFile(userId: string): Promise<void> {
    try {
      // Get the current user's file metadata from Firestore
      const userDocRef = doc(this.firestore, 'users', userId);
      const fileDocRef = doc(this.firestore, 'files', userId);
      const fileDoc = await getDoc(fileDocRef);
      if (fileDoc.exists()) {
        // If there's an existing file, delete it from storage
        const existingFilePath = fileDoc.data()['filePath'];
        const existingFileRef = ref(this.storage, existingFilePath);
        await deleteObject(existingFileRef);
        await deleteDoc(fileDocRef);
        updateDoc(userDocRef, { photoUrl: null })
          .catch((error) => {
            this.notifierService.showInformation(`Error removing photo URL from user document: ${{error}}`);
          });
      } else {
        this.notifierService.showInformation('No file metadata found to delete');
      }
    } catch (error) {
      this.notifierService.showInformation(`Error deleting file: ${{error}}`);
    }
  }

  // Helper function to compress an image
  private compressImage(file: File): Promise<File> {
    return new Promise((resolve, reject) => {
      const image = new Image();
      image.src = URL.createObjectURL(file);
      image.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        if (!ctx) {
          reject(new Error('Failed to get canvas context'));
          return;
        }
        const maxWidth = 1024; // Set the maximum width you want for the images
        const maxHeight = 1024; // Set the maximum height you want for the images
        let width = image.width;
        let height = image.height;

        if (width > height) {
          if (width > maxWidth) {
            height *= maxWidth / width;
            width = maxWidth;
          }
        } else {
          if (height > maxHeight) {
            width *= maxHeight / height;
            height = maxHeight;
          }
        }

        canvas.width = width;
        canvas.height = height;
        ctx.drawImage(image, 0, 0, width, height);

        // Function to attempt to compress the image
        const attemptCompression = (quality: number, resolve: (file: File) => void, reject: (reason: Error) => void) => {
            canvas.toBlob(
            (blob) => {
              if (blob === null) {
                reject(new Error('Failed to create blob from canvas'));
                return;
              }
              if (blob.size <= 30 * 1024) {
                // If the blob size is less than or equal to 100 KB, resolve the promise
                const compressedFile = new File([blob], file.name, {
                  type: 'image/jpeg',
                  lastModified: Date.now(),
                });
                resolve(compressedFile);
              } else if (quality > 0.1) {
                // If the quality can still be reduced, try again with lower quality
                attemptCompression(quality - 0.1, resolve, reject);
              } else {
                // If the quality is already too low, reject the promise
                reject(new Error('Cannot compress image to the desired size'));
              }
            },
            'image/jpeg',
            quality
          );
        };

        // Start the compression attempt with an initial quality value
        attemptCompression(0.5, resolve, reject);
      };

      image.onerror = () => {
        reject(new Error('Image loading failed'));
      };
    });
  }
}
