'use strict';

const { expect } = require('chai');
const log = require('log').get('serverless:test');
const fixtures = require('../../fixtures/programmatic');

const awsRequest = require('@serverless/test/aws-request');
const crypto = require('crypto');
const { deployService, removeService } = require('../../utils/integration');
const {
  isDependencyStackAvailable,
  getDependencyStackOutputMap,
} = require('../../utils/cloudformation');

const EFS_MAX_PROPAGATION_TIME = 1000 * 60 * 5;

const retryableMountErrors = new Set([
  'EFSMountFailureException',
  'EFSMountTimeoutException',
  'EFSIOException',
]);

describe('AWS - FileSystemConfig Integration Test', function () {
  this.timeout(1000 * 60 * 100); // Involves time-taking deploys
  let stackName;
  let startTime;
  let serviceDir;
  const stage = 'dev';
  const filename = `/mnt/testing/${crypto.randomBytes(8).toString('hex')}.txt`;

  before(async () => {
    const isDepsStackAvailable = await isDependencyStackAvailable();
    if (!isDepsStackAvailable) {
      throw new Error('CloudFormation stack with integration test dependencies not found.');
    }

    const outputMap = await getDependencyStackOutputMap();

    const fileSystemConfig = {
      localMountPath: '/mnt/testing',
      arn: outputMap.get('EFSAccessPointARN'),
    };

    const serviceData = await fixtures.setup('functionEfs', {
      configExt: {
        provider: {
          vpc: {
            subnetIds: [outputMap.get('PrivateSubnetA')],
            securityGroupIds: [outputMap.get('SecurityGroup')],
          },
          environment: {
            FILENAME: filename,
          },
        },
        functions: { writer: { fileSystemConfig }, reader: { fileSystemConfig } },
      },
    });
    ({ servicePath: serviceDir } = serviceData);

    const serviceName = serviceData.serviceConfig.service;
    stackName = `${serviceName}-${stage}`;
    await deployService(serviceDir);
    startTime = Date.now();
  });

  after(async () => {
    if (serviceDir) {
      await removeService(serviceDir);
    }
  });

  it('should be able to write to efs and read from it in a separate function', async function self() {
    try {
      await awsRequest('Lambda', 'invoke', {
        FunctionName: `${stackName}-writer`,
        InvocationType: 'RequestResponse',
      });
    } catch (e) {
      // Sometimes EFS is not available right away which causes invoke to fail,
      // here we retry it to avoid that issue
      if (retryableMountErrors.has(e.code) && Date.now() - startTime < EFS_MAX_PROPAGATION_TIME) {
        log.warn('Failed to invoke, retry');
        return self();
      }
      throw e;
    }

    const readerResult = await awsRequest('Lambda', 'invoke', {
      FunctionName: `${stackName}-reader`,
      InvocationType: 'RequestResponse',
    });
    const payload = JSON.parse(readerResult.Payload);

    expect(payload).to.deep.equal({ result: 'fromlambda' });
    return null;
  });
});
