var mongoose = require('mongoose');
var _ = require('lodash');

var Schema = mongoose.Schema;

var customField = {
  _id: false,
  customField: { type: Schema.Types.ObjectId, ref: 'CustomField' },
  text: Schema.Types.Mixed,
};

var VulnerabilityUpdateSchema = new Schema(
  {
    vulnerability: {
      type: Schema.Types.ObjectId,
      ref: 'Vulnerability',
      required: true,
    },
    creator: { type: Schema.Types.ObjectId, ref: 'User', required: true },
    cvssv3: String,
    priority: { type: Number, enum: [1, 2, 3, 4] },
    remediationComplexity: { type: Number, enum: [1, 2, 3] },
    references: [String],
    locale: String,
    title: String,
    vulnType: String,
    description: String,
    observation: String,
    remediation: String,
    category: String,
    customFields: [customField],
  },
  { timestamps: true },
);

/*
 *** Statics ***
 */

// Get all vulnerabilities
VulnerabilityUpdateSchema.statics.getAll = () => {
  return new Promise((resolve, reject) => {
    var query = VulnerabilityUpdate.find();
    query
      .exec()
      .then(rows => {
        resolve(rows);
      })
      .catch(err => {
        reject(err);
      });
  });
};

// Get all updates of vulnerability
VulnerabilityUpdateSchema.statics.getAllByVuln = vulnId => {
  return new Promise((resolve, reject) => {
    var query = VulnerabilityUpdate.find({ vulnerability: vulnId });
    query.populate('creator', '-_id username');
    query.populate('customFields.customField', 'fieldType label');
    query
      .exec()
      .then(rows => {
        resolve(rows);
      })
      .catch(err => {
        reject(err);
      });
  });
};

// Create vulnerability
VulnerabilityUpdateSchema.statics.create = (username, vulnerability) => {
  return new Promise((resolve, reject) => {
    var created = true;
    var User = mongoose.model('User');
    var creator = '';
    var Vulnerability = mongoose.model('Vulnerability');
    var query = User.findOne({ username: username });
    query
      .exec()
      .then(row => {
        if (row) {
          creator = row._id;
          var query = Vulnerability.findOne({
            'details.title': vulnerability.title,
          });
          return query.exec();
        } else throw { fn: 'NotFound', message: 'User not found' };
      })
      .then(row => {
        if (row) {
          if (row.status === 1)
            throw {
              fn: 'Forbidden',
              message: 'Vulnerability not approved yet',
            };
          else {
            // Check if there are any changes from the original vulnerability
            var detail = row.details.find(
              d => d.locale === vulnerability.locale,
            );
            // console.log(vulnerability.customFields)
            // console.log(detail.customFields)
            if (
              typeof detail !== 'undefined' &&
              (row.cvssv3 || '').includes(vulnerability.cvssv3) &&
              vulnerability.priority === (row.priority || null) &&
              vulnerability.remediationComplexity ===
                (row.remediationComplexity || null) &&
              _.isEqual(vulnerability.references, detail.references || []) &&
              vulnerability.category === (row.category || null) &&
              vulnerability.vulnType === (detail.vulnType || null) &&
              vulnerability.description === (detail.description || null) &&
              vulnerability.observation === (detail.observation || null) &&
              vulnerability.remediation === (detail.remediation || null) &&
              vulnerability.customFields.length ===
                detail.customFields.length &&
              vulnerability.customFields.every((e, idx) => {
                return (
                  e.customField._id == detail.customFields[idx].customField &&
                  e.text === detail.customFields[idx].text
                );
              })
            ) {
              throw {
                fn: 'BadParameters',
                message: 'No changes from the original vulnerability',
              };
            }
            vulnerability.vulnerability = row._id;
            vulnerability.creator = creator;
            var query = new VulnerabilityUpdate(vulnerability);
            created = false;
            return query.save();
          }
        } else {
          var vuln = {};
          vuln.cvssv3 = vulnerability.cvssv3 || null;
          vuln.priority = vulnerability.priority || null;
          vuln.remediationComplexity =
            vulnerability.remediationComplexity || null;
          vuln.category = vulnerability.category || null;
          vuln.creator = creator;
          var details = {};
          details.locale = vulnerability.locale || null;
          details.title = vulnerability.title || null;
          details.vulnType = vulnerability.vulnType || null;
          details.description = vulnerability.description || null;
          details.observation = vulnerability.observation || null;
          details.remediation = vulnerability.remediation || null;
          details.references = vulnerability.references || null;
          details.customFields = vulnerability.customFields || [];
          vuln.details = [details];
          var query = new Vulnerability(vuln);
          return query.save();
        }
      })
      .then(row => {
        if (created) resolve('Finding created as new Vulnerability');
        else {
          var query = Vulnerability.findOneAndUpdate(
            { 'details.title': vulnerability.title },
            { status: 2 },
          );
          return query.exec();
        }
      })
      .then(row => {
        resolve('Update proposed for existing vulnerability');
      })
      .catch(err => {
        reject(err);
      });
  });
};

VulnerabilityUpdateSchema.statics.deleteAllByVuln = async vulnId => {
  return await VulnerabilityUpdate.deleteMany({ vulnerability: vulnId });
};

/*
 *** Methods ***
 */

var VulnerabilityUpdate = mongoose.model(
  'VulnerabilityUpdate',
  VulnerabilityUpdateSchema,
);
module.exports = VulnerabilityUpdate;