import crypto from "crypto";
import sharp from "sharp";
import { FileInterface } from "../../../models/file-model";
import { UserInterface } from "../../../models/user-model";
import { v4 as uuidv4 } from "uuid";
import env from "../../../enviroment/env";
import { createGenericParams } from "./storageHelper";
import { S3Actions } from "../actions/S3-actions";
import { FilesystemActions } from "../actions/file-system-actions";
import { EventEmitter } from "stream";
import { getFileDB, getThumbnailDB } from "../../../db/dbFactory";
import { getStorageActions } from "../actions/helper-actions";
import { getFSStoragePath } from "../../../utils/getFSStoragePath";

const fileDB = getFileDB();
const thumbnailDB = getThumbnailDB();

const storageActions = getStorageActions();

const processData = (
  file: FileInterface,
  filename: string,
  user: UserInterface
) => {
  const eventEmitter = new EventEmitter();

  try {
    const password = user.getEncryptionKey();

    let CIPHER_KEY = crypto.createHash("sha256").update(password!).digest();

    const thumbnailFilename = uuidv4();

    const thumbnailIV = crypto.randomBytes(16);

    const params = createGenericParams({
      filePath: file.metadata.filePath,
      Key: file.metadata.s3ID,
    });

    const readStream = storageActions.createReadStream(params);

    const { writeStream, emitter } = storageActions.createWriteStream(
      params,
      readStream,
      thumbnailFilename
    );

    const decipher = crypto.createDecipheriv(
      "aes256",
      CIPHER_KEY,
      file.metadata.IV
    );

    const imageResize = sharp().resize(300);

    const handleFinish = async () => {
      const thumbnailModel = await thumbnailDB.createThumbnail({
        name: filename,
        owner: user._id.toString(),
        IV: thumbnailIV,
        path: getFSStoragePath() + thumbnailFilename,
        s3ID: thumbnailFilename,
      });

      const updatedFile = await fileDB.setThumbnail(
        file._id!.toString(),
        thumbnailModel._id.toString()
      );

      if (!updatedFile) {
        throw new Error("Thumbnail Not Set");
      }

      eventEmitter.emit("finish", updatedFile);
    };

    const handleError = (e: Error) => {
      eventEmitter.emit("error", e);
    };

    readStream.on("error", handleError);

    writeStream.on("error", handleError);

    decipher.on("error", handleError);

    imageResize.on("error", handleError);

    const thumbnailCipher = crypto.createCipheriv(
      "aes256",
      CIPHER_KEY,
      thumbnailIV
    );

    readStream
      .pipe(decipher)
      .pipe(imageResize)
      .pipe(thumbnailCipher)
      .pipe(writeStream);

    if (emitter) {
      emitter.on("finish", handleFinish);
    } else {
      writeStream.on("finish", handleFinish);
    }
  } catch (e) {
    eventEmitter.emit("error", e);
  }

  return eventEmitter;
};

const createThumbnail = (
  file: FileInterface,
  filename: string,
  user: UserInterface
) => {
  return new Promise<FileInterface>((resolve, _) => {
    const eventEmitter = processData(file, filename, user);
    eventEmitter.on("error", (e) => {
      console.log("Error creating thumbnail", e);
      resolve(file);
    });
    eventEmitter.on("finish", (updatedFile: FileInterface) => {
      resolve(updatedFile);
    });
  });
};

export default createThumbnail;
