'use strict';

const waterfall   = require('waterfall');
const iam         = require('./iam.js');
const arn         = require('./arn.js');

const maxManagedPolicyVersions  = 5;
const resourceManagedPolicyPath = '/resource/';

// builds a policy document from a list of standard asset objects
const policies = module.exports = {
  createManagedPolicy: function(name, document, callback) {
    let description = `Allows downstream resources to invoke ${name}. Generated by betty.`;
    let policyArn = policies.createManagedPolicyArnForResource(name);
    global.log.debug({ name, description, document, policyArn }, 'creating managed policy');
    waterfall({
      exists: (state, next) => {
        global.log.trace({ policyArn }, 'checking if managed policy exists');
        policies.getPolicy(policyArn, next);
      },
      policy: (state, next) => {
        if (state.exists) {
          global.log.trace({ policyArn }, 'exists');
          policies._updateManagedPolicy(policyArn, document, next);
        }
        else {
          global.log.trace({ policyArn }, 'does not exist');
          policies._createManagedPolicy(name, description, document, next);
        }
      },
    }, (err, state) => {
      if (err) {
        return callback(err);
      }
      global.log.trace({ policy: state.policy }, 'created managed policy');
      callback(null, state.policy);
    });
  },

  _deleteOldestVersion: function(policyArn, versions, callback) {
    let oldestVersionId;
    let oldestVersionTimestamp = null;
    for (let i=0; i < versions.length; i++) {
      let version     = versions[i];
      let versionDate = new Date(version.CreateDate).getTime();
      global.log.trace({ version, versionDate, oldestVersionTimestamp, isOlder: versionDate < oldestVersionTimestamp }, 'checking if older version');
      if (null === oldestVersionTimestamp || versionDate < oldestVersionTimestamp) {
        global.log.trace({ version, versionDate }, 'new oldest found');
        oldestVersionId         = version.VersionId;
        oldestVersionTimestamp  = versionDate;
      }
    }
    global.log.trace({ versions, oldestVersionId, oldestVersionTimestamp }, 'deleting oldest policy version');
    policies.deleteManagedPolicyVersion(policyArn, oldestVersionId, (err) => {
      if (err) {
        global.log.error({ err }, 'error deleting oldest policy');
        return callback(err);
      }
      callback(null);
    });
  },

  _createManagedPolicy: function(name, description, document, callback) {
    let params = {
      PolicyName:       name,
      Description:      description,
      PolicyDocument:   JSON.stringify(document, null, 2),
      Path:             resourceManagedPolicyPath,
    };
    iam.createPolicy(params, callback);
  },

  _updateManagedPolicy: function(policyArn, document, callback) {
    let update = (err) => {
      if (err) return callback(err);
      let params = {
        PolicyArn:        policyArn,
        PolicyDocument:   JSON.stringify(document, null, 2),
        SetAsDefault:     true,
      };
      iam.createPolicyVersion(params, callback);
    };
    policies.listManagedPolicyVersions(policyArn, (err, data) => {
      if (err) {
        global.log.warn({ err }, 'unable to check existing versions');
      }
      let versions = data.Versions || [];
      if (versions.length >= maxManagedPolicyVersions) {
        policies._deleteOldestVersion(policyArn, versions, update);
      }
      else {
        update();
      }
    });
  },

  deleteManagedPolicyVersion: function(policyArn, versionId, callback) {
    let params = {
      PolicyArn:        policyArn,
      VersionId:        versionId,
    };
    iam.deletePolicyVersion(params, callback);
  },

  listManagedPolicyVersions: function(policyArn, callback) {
    let params = {
      PolicyArn:        policyArn,
    };
    iam.listPolicyVersions(params, callback);
  },

  getPolicy: function(policyArn, callback) {
    let params = {
      PolicyArn:    policyArn,
    };
    iam.getPolicy(params, (err, data) => {
      if (err) {
        return callback(null, null); // err probably means it doesn't exist and should be created
      }
      callback(null, data);
    });
  },

  createManagedPolicyArnForResource: function(name) {
    let policyArn = arn.make({
      service:    'iam',
      account:    global.betty.aws.accountId,
      resource:   `policy${resourceManagedPolicyPath}${name}`,
    });
    return policyArn;
  },

  nameFromAsset: function(namePrefix, asset) {
    let name = asset.name.split(/[^\w\d-]/)[0];
    return `${namePrefix}-${asset.service}-${name}`;
  },

  documentFromAsset: function(asset) {
    return policies.documentFromAssets([ asset ]);
  },

  documentFromAssets: function(assets) {
    return {
      Version:      '2012-10-17',
      Statement:    assets.map(asset => policies.statementFromAsset(asset)),
    };
  },

  statementFromAsset: function(asset) {
    let regions;
    if (asset.hasOwnProperty('region')) {
      regions = Array.isArray(asset.region) ? asset.region : [ asset.region ];
    }
    else {
      regions = [ global.betty.aws.region ];
    }
    return {
      Effect:   'Allow',
      Action:   asset.permissions,
      Resource: regions.map(region => {
        let resource =  arn.make({
          region:   region,
          service:  asset.service,
          account:  global.betty.aws.accountId,
          resource: asset.name,
        });
        return resource;
      }),
    };
  },
};

@@ -4516,16 +4516,17 @@
  name = 
+(
 asset.na
@@ -4548,17 +4548,267 @@
 d-%5D/)%5B0%5D
-;
+ %7C%7C '').trim();%0A    if (!name.length) %7B // might be %22service%22: %22*%22%0A      name = asset.name.replace(/%5C*/g, 'wildcard').replace(/%5B%5E%5Cw%5Cd%5D/g, '').trim();%0A    %7D%0A    if (!name.length) %7B // if that fails, just repeat service%0A      name = asset.service;%0A    %7D
 %0A    ret

fix role naming prob with wildcards