"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _utils = require("@rivalis/utils");

var _database = require("../database");

var _FleetService = _interopRequireDefault(require("./FleetService"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

/**
 * @typedef SecurityMethod
 * @property {string} kind
 * @property {string} value
 */
class AppService {
  constructor() {
    _defineProperty(this, "fleetService", _FleetService.default.getInstance());

    _defineProperty(this, "securityMethods", ['JWT', 'Steam']);

    _defineProperty(this, "logger", _utils.Logger.getLogger('app service'));
  }

  async getAll() {
    let apps = await _database.Models.Apps.findAll();
    let appList = [];

    for (let app of apps) {
      const {
        id,
        name,
        active,
        secured
      } = app.get();
      const fleets = [];
      this.fleetService.fleets.filter(fleet => fleet.appId === id).forEach(fleet => {
        fleets.push(fleet);
      });
      const security = [];
      let methods = await _database.Models.AppSecurity.findAll({
        where: {
          appId: id
        }
      });
      methods.forEach(method => {
        const {
          kind,
          value
        } = method.get();
        security.push({
          kind,
          value
        });
      });
      appList.push({
        id,
        name,
        active,
        secured,
        fleets,
        security
      });
    }

    return appList;
  }
  /**
   * 
   * @param {string} id 
   */


  async getOne(id) {
    let app = await _database.Models.Apps.findOne({
      where: {
        id
      }
    });

    if (app === null) {
      throw new Error(`app id="${id}" doesn not exist`);
    }

    const {
      name,
      secured,
      active
    } = app.get();
    const fleets = [];
    this.fleetService.fleets.filter(fleet => fleet.appId === id).forEach(fleet => {
      fleets.push(fleet);
    });
    const security = [];
    let methods = await _database.Models.AppSecurity.findAll({
      where: {
        appId: id
      }
    });
    methods.forEach(method => {
      const {
        kind,
        value
      } = method.get();
      security.push({
        kind,
        value
      });
    });
    return {
      id,
      name,
      active,
      secured,
      fleets,
      security
    };
  }
  /**
   * 
   * @param {string} name 
   * @param {string} active 
   * @param {boolean} secured 
   * @param {Array<string>} fleets 
   * @param {Array<SecurityMethod>} security 
   */


  async create(name, active, secured, fleets, security) {
    this.validate(name, active, secured, fleets, security);
    return (0, _database.getInstance)().transaction(async () => {
      let id = (0, _utils.generateId)(12);

      let app = _database.Models.Apps.build({
        id,
        name,
        active,
        secured
      });

      await app.save();

      for (let method of security) {
        let security = _database.Models.AppSecurity.build({
          appId: id,
          kind: method.kind,
          value: method.value
        });

        await security.save();
      }

      for (let fleetId of fleets) {
        await this.fleetService.assignFleet(fleetId, id);
      }

      return app.get();
    }).then(() => {
      this.fleetService.invokeUpdate();
    }).catch(error => {
      throw new Error(`internal server error: ${error.message}`);
    });
  }
  /**
   * @param {string} id
   * @param {string} name 
   * @param {string} active 
   * @param {boolean} secured 
   * @param {Array<string>} fleets 
   * @param {Array<SecurityMethod>} security 
   */


  async update(id, name, active, secured, fleets, security) {
    this.validate(name, active, secured, fleets, security);
    return (0, _database.getInstance)().transaction(async () => {
      let app = await _database.Models.Apps.findOne({
        where: {
          id
        }
      });

      if (app === null) {
        throw new Error(`app id="${id}" does not exist`);
      }

      app.set('name', name);
      app.set('active', active);
      app.set('secured', secured);
      let assignedFleets = this.fleetService.fleets.filter(fleet => fleet.appId === id);

      for (let fleet of assignedFleets) {
        await this.fleetService.freeFleet(fleet.id);
      }

      let oldSecurityMethods = await _database.Models.AppSecurity.findAll({
        where: {
          appId: id
        }
      });

      for (let securityMethod of oldSecurityMethods) {
        await securityMethod.destroy();
      }

      for (let method of security) {
        let security = _database.Models.AppSecurity.build({
          appId: id,
          kind: method.kind,
          value: method.value
        });

        await security.save();
      }

      for (let fleetId of fleets) {
        await this.fleetService.assignFleet(fleetId, id);
      }

      await app.save();
      this.fleetService.invokeUpdate();
      return app.get();
    });
  }

  async delete(id) {
    return (0, _database.getInstance)().transaction(async () => {
      let app = await _database.Models.Apps.findOne({
        where: {
          id
        }
      });

      if (app === null) {
        throw new Error(`app id="${id}" does not exist`);
      }

      let assignedFleets = this.fleetService.fleets.filter(fleet => fleet.appId === id);

      for (let fleet of assignedFleets) {
        await this.fleetService.freeFleet(fleet.id);
      }

      let oldSecurityMethods = await _database.Models.AppSecurity.findAll({
        where: {
          appId: id
        }
      });

      for (let securityMethod of oldSecurityMethods) {
        await securityMethod.destroy();
      }

      await app.destroy();
      this.fleetService.invokeUpdate();
      return null;
    });
  }
  /**
   * 
   * @param {string} name 
   * @param {string} active 
   * @param {boolean} secured 
   * @param {Array<string>} fleets 
   * @param {Array<SecurityMethod>} security 
   */


  validate(name, active, secured, fleets, security) {
    if (typeof name !== 'string' || name.length < 3) {
      throw new Error('"name" must be a string and greater than 3 characters');
    }

    if (typeof active !== 'boolean') {
      throw new Error('"active" must be a boolean type');
    }

    if (typeof secured !== 'boolean') {
      throw new Error('"secured" must be a boolean type');
    }

    if (!Array.isArray(fleets)) {
      throw new Error('"fleets" must be an array of strings (fleet ids)');
    }

    if (fleets.length === 0) {
      throw new Error('application can not be created without assigned fleet(s) of instances');
    }

    for (let [index, fleetId] of fleets.entries()) {
      const [fleet = null] = this.fleetService.fleets.filter(fleet => fleet.id === fleetId);

      if (fleet === null) {
        throw new Error(`"fleets[${index}]" fleet id=(${fleetId}) does not exist`);
      }
    }

    if (!Array.isArray(security)) {
      throw new Error('"security" must be an array of Objects { kind, value }');
    }

    if (secured && security.length === 0) {
      throw new Error('server can not be marked as secured without any security method');
    }

    if (!secured && security.length > 0) {
      throw new Error('"security" can be applied only on secured servers');
    }

    for (let [index, method] of security.entries()) {
      if (typeof method !== 'object') {
        throw new Error(`"security[${index}]" is not a object`);
      }

      if (!this.securityMethods.includes(method.kind)) {
        throw new Error(`"security[${index}]" type=(${method.kind}) is not allowed`);
      }
    }
  }

}
/** @type {AppService} */


let instance = null;

AppService.getInstance = () => {
  if (instance === null) {
    instance = new AppService();
  }

  return instance;
};

var _default = AppService;
exports.default = _default;