'use strict';
const FileSystemAdapter = require('../index.js');
const fs = require('fs');
const path = require('path');

describe('File encryption tests', () => {
  const directory = 'sub1/sub2';

  afterEach(function() {
    const filePath = path.join('files', directory);
    const fileNames = fs.readdirSync(filePath);
    fileNames.filter(fileName => fileName.indexOf('.') === 0);
    fileNames.forEach(fileName => {
      fs.unlinkSync(path.join(filePath, fileName));
    })
  });

  it('should create file location based on config', async function () {
    const fsAdapter = new FileSystemAdapter();
    const config = {mount: '/parse', applicationId: 'yolo'}
    const location = fsAdapter.getFileLocation(config, 'hello.txt')
    expect(location).toBe('/parse/files/yolo/hello.txt');
  }, 5000)

  it("should save encrypted file in default directory", async function() {
    const adapter = new FileSystemAdapter({
      encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
    });
    const filename = 'file2.txt';
    const filePath = 'files/' + filename;
    await adapter.createFile(filename, "hello world", 'text/utf8');
    const result = await adapter.getFileData(filename);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual("hello world");
    const data = fs.readFileSync(filePath);
    expect(data.toString('utf-8')).not.toEqual("hello world");
  }, 5000);

  it("should save encrypted file in specified directory", async function() {
    const adapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
    });
    const filename = 'file2.txt';
    const filePath = 'files/' + directory + '/' + filename;
    await adapter.createFile(filename, "hello world", 'text/utf8');
    const result = await adapter.getFileData(filename);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual("hello world");
    const data = fs.readFileSync(filePath);
    expect(data.toString('utf-8')).not.toEqual("hello world");
  }, 5000);

  it("should save encrypted file in specified directory when directory starts with /", async function() {
    const adapter = new FileSystemAdapter({
      filesSubDirectory: '/sub1/sub2',
      encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
    });
    const filename = 'file2.txt';
    const filePath = 'files/' + directory + '/' + filename;
    await adapter.createFile(filename, "hello world", 'text/utf8');
    const result = await adapter.getFileData(filename);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual("hello world");
    const data = fs.readFileSync(filePath);
    expect(data.toString('utf-8')).not.toEqual("hello world");
  }, 5000);

  it("should rotate key of all unencrypted files to encrypted files", async function() {
    const unEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory
    });
    const encryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
    });
    const fileName1 = 'file1.txt';
    const data1 = "hello world";
    const fileName2 = 'file2.txt';
    const data2 = "hello new world";
    const filePath1 = 'files/' + directory + '/' + fileName1;
    const filePath2 = 'files/' + directory + '/' + fileName2;
    // Store unecrypted files
    await unEncryptedAdapter.createFile(fileName1, data1, 'text/utf8');
    let result = await unEncryptedAdapter.getFileData(fileName1);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data1);
    const unEncryptedData1 = fs.readFileSync(filePath1);
    await unEncryptedAdapter.createFile(fileName2, data2, 'text/utf8');
    result = await unEncryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const unEncryptedData2 = fs.readFileSync(filePath2);
    // Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
    const {rotated, notRotated} =  await encryptedAdapter.rotateEncryptionKey();
    expect(rotated.length).toEqual(2);
    expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
    expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
    expect(notRotated.length).toEqual(0);
    result = await encryptedAdapter.getFileData(fileName1);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data1);
    const encryptedData1 = fs.readFileSync(filePath1);
    expect(encryptedData1.toString('utf-8')).not.toEqual(unEncryptedData1);
    result = await encryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const encryptedData2 = fs.readFileSync(filePath2);
    expect(encryptedData2.toString('utf-8')).not.toEqual(unEncryptedData2);
  }, 5000);

  it("should rotate key of all old encrypted files to files encrypted with a new key", async function() {
    const oldEncryptionKey = 'oldKeyThatILoved';
    const oldEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: oldEncryptionKey
    });
    const encryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: 'newKeyThatILove'
    });
    const fileName1 = 'file1.txt';
    const data1 = "hello world";
    const fileName2 = 'file2.txt';
    const data2 = "hello new world";
    const filePath1 = 'files/' + directory + '/' + fileName1;
    const filePath2 = 'files/' + directory + '/' + fileName2;
    // Store original encrypted files
    await oldEncryptedAdapter.createFile(fileName1, data1, 'text/utf8');
    let result = await oldEncryptedAdapter.getFileData(fileName1);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data1);
    const oldEncryptedData1 = fs.readFileSync(filePath1);
    await oldEncryptedAdapter.createFile(fileName2, data2, 'text/utf8');
    result = await oldEncryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const oldEncryptedData2 = fs.readFileSync(filePath2);
    // Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
    const {rotated, notRotated} =  await encryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey});
    expect(rotated.length).toEqual(2);
    expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
    expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
    expect(notRotated.length).toEqual(0);
    const result2 = await encryptedAdapter.getFileData(fileName1);
    expect(result2 instanceof Buffer).toBe(true);
    expect(result2.toString('utf-8')).toEqual(data1);
    const encryptedData1 = fs.readFileSync(filePath1);
    expect(encryptedData1.toString('utf-8')).not.toEqual(oldEncryptedData1);
    result = await encryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const encryptedData2 = fs.readFileSync(filePath2);
    expect(encryptedData2.toString('utf-8')).not.toEqual(oldEncryptedData2);
  }, 5000);

  it("should rotate key of all old encrypted files to unencrypted files", async function() {
    const oldEncryptionKey = 'oldKeyThatILoved';
    const oldEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: oldEncryptionKey
    });
    const unEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory
    });
    const fileName1 = 'file1.txt';
    const data1 = "hello world";
    const fileName2 = 'file2.txt';
    const data2 = "hello new world";
    const filePath1 = 'files/' + directory + '/' + fileName1;
    const filePath2 = 'files/' + directory + '/' + fileName2;
    // Store original encrypted files
    await oldEncryptedAdapter.createFile(fileName1, data1, 'text/utf8');
    let result = await oldEncryptedAdapter.getFileData(fileName1);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data1);
    const oldEncryptedData1 = fs.readFileSync(filePath1);
    await oldEncryptedAdapter.createFile(fileName2, data2, 'text/utf8');
    result = await oldEncryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const oldEncryptedData2 = fs.readFileSync(filePath2);
    // Check if unEncrypted adapter can read data and make sure it's not the same as oldEncrypted adapter
    const {rotated, notRotated} =  await unEncryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey});
    expect(rotated.length).toEqual(2);
    expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
    expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
    expect(notRotated.length).toEqual(0);
    const result2 = await unEncryptedAdapter.getFileData(fileName1);
    expect(result2 instanceof Buffer).toBe(true);
    expect(result2.toString('utf-8')).toEqual(data1);
    const encryptedData1 = fs.readFileSync(filePath1);
    expect(encryptedData1.toString('utf-8')).not.toEqual(oldEncryptedData1);
    result = await unEncryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const encryptedData2 = fs.readFileSync(filePath2);
    expect(encryptedData2.toString('utf-8')).not.toEqual(oldEncryptedData2);
  }, 5000);

  it("should only encrypt specified fileNames with the new key", async function() {
    const oldEncryptionKey = 'oldKeyThatILoved';
    const oldEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: oldEncryptionKey
    });
    const encryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: 'newKeyThatILove'
    });
    const unEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory
    });
    const fileName1 = 'file1.txt';
    const data1 = "hello world";
    const fileName2 = 'file2.txt';
    const data2 = "hello new world";
    const filePath1 = 'files/' + directory + '/' + fileName1;
    const filePath2 = 'files/' + directory + '/' + fileName2;
    // Store original encrypted files
    await oldEncryptedAdapter.createFile(fileName1, data1, 'text/utf8');
    let result = await oldEncryptedAdapter.getFileData(fileName1);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data1);
    const oldEncryptedData1 = fs.readFileSync(filePath1);
    await oldEncryptedAdapter.createFile(fileName2, data2, 'text/utf8');
    result = await oldEncryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const oldEncryptedData2 = fs.readFileSync(filePath2);
    // Inject unecrypted file to see if causes an issue
    const fileName3 = 'file3.txt';
    const data3 = "hello past world";
    await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8');
    // Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
    const {rotated, notRotated} =  await encryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey, fileNames: [fileName1,fileName2]});
    expect(rotated.length).toEqual(2);
    expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
    expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
    expect(notRotated.length).toEqual(0);
    expect(rotated.filter(function(value){ return value === fileName3;}).length).toEqual(0);
    const result2 = await encryptedAdapter.getFileData(fileName1);
    expect(result2 instanceof Buffer).toBe(true);
    expect(result2.toString('utf-8')).toEqual(data1);
    const encryptedData1 = fs.readFileSync(filePath1);
    expect(encryptedData1.toString('utf-8')).not.toEqual(oldEncryptedData1);
    result = await encryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const encryptedData2 = fs.readFileSync(filePath2);
    expect(encryptedData2.toString('utf-8')).not.toEqual(oldEncryptedData2);
  }, 5000);

  it("should return fileNames of those it can't encrypt with the new key", async function() {
    const oldEncryptionKey = 'oldKeyThatILoved';
    const oldEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: oldEncryptionKey
    });
    const encryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory,
      encryptionKey: 'newKeyThatILove'
    });
    const unEncryptedAdapter = new FileSystemAdapter({
      filesSubDirectory: directory
    });
    const fileName1 = 'file1.txt';
    const data1 = "hello world";
    const fileName2 = 'file2.txt';
    const data2 = "hello new world";
    const filePath1 = 'files/' + directory + '/' + fileName1;
    const filePath2 = 'files/' + directory + '/' + fileName2;
    // Store original encrypted files
    await oldEncryptedAdapter.createFile(fileName1, data1, 'text/utf8');
    let result = await oldEncryptedAdapter.getFileData(fileName1);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data1);
    const oldEncryptedData1 = fs.readFileSync(filePath1);
    await oldEncryptedAdapter.createFile(fileName2, data2, 'text/utf8');
    result = await oldEncryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const oldEncryptedData2 = fs.readFileSync(filePath2);
    // Inject unecrypted file to cause an issue
    const fileName3 = 'file3.txt';
    const data3 = "hello past world";
    await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8');
    result = await unEncryptedAdapter.getFileData(fileName3);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data3);
    // Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
    const {rotated, notRotated} =  await encryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey});
    expect(rotated.length).toEqual(2);
    expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
    expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
    expect(notRotated.length).toEqual(1);
    expect(notRotated.filter(function(value){ return value === fileName3;}).length).toEqual(1);
    const result2 = await encryptedAdapter.getFileData(fileName1);
    expect(result2 instanceof Buffer).toBe(true);
    expect(result2.toString('utf-8')).toEqual(data1);
    const encryptedData1 = fs.readFileSync(filePath1);
    expect(encryptedData1.toString('utf-8')).not.toEqual(oldEncryptedData1);
    result = await encryptedAdapter.getFileData(fileName2);
    expect(result instanceof Buffer).toBe(true);
    expect(result.toString('utf-8')).toEqual(data2);
    const encryptedData2 = fs.readFileSync(filePath2);
    expect(encryptedData2.toString('utf-8')).not.toEqual(oldEncryptedData2);
  }, 5000);
})
