/*
 * Copyright (C) 2019 StApps
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
import {
  SCNotFoundErrorResponse,
  SCPluginAlreadyRegisteredErrorResponse,
  SCPluginMetaData,
  SCPluginRegisterRequest,
  SCPluginRegisterResponse,
  SCPluginRegisterRoute,
} from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import {deepStrictEqual} from 'assert';
import {isTestEnvironment, plugins} from '../common.js';
import {createRoute} from './route.js';
import {backendConfig} from '../config.js';

/**
 * Contains information for using the route for registering routes
 */
const pluginRegisterRouteModel = new SCPluginRegisterRoute();

/**
 * Implementation of the plugin registration route (SCPluginRegisterRoute)
 */
export const pluginRegisterRouter = createRoute(pluginRegisterRouteModel, pluginRegisterHandler);

/**
 * Handles requests on route for registering plugins
 * @param request Request received for registering or unregistering a plugin
 * @param _app Express application
 */
export async function pluginRegisterHandler(
  request: SCPluginRegisterRequest,
  _app: Express.Application,
): Promise<SCPluginRegisterResponse> {
  switch (request.action) {
    case 'add': {
      return addPlugin(request.plugin);
    }
    case 'remove': {
      return removePlugin(request.route);
    }
  }
}

/**
 * Adds a plugin to the list (map) of registered plugins
 * @param plugin Meta data of the plugin
 */
function addPlugin(plugin: SCPluginMetaData): SCPluginRegisterResponse {
  // check if plugin (its route) has already been registered
  if (plugins.has(plugin.route)) {
    const previouslyRegistered = plugins.get(plugin.route);
    try {
      deepStrictEqual(previouslyRegistered, plugin);

      return {success: true};
    } catch {
      throw new SCPluginAlreadyRegisteredErrorResponse(
        'Plugin already registered',
        plugins.get(plugin.route)!,
        isTestEnvironment,
      );
    }
  }
  // it's a new plugin so it can be added to the map of plugins
  plugins.set(plugin.route, plugin);
  // add plugin  info to app config
  if (backendConfig.app.features.plugins === undefined) {
    backendConfig.app.features.plugins = {};
  }
  backendConfig.app.features.plugins[plugin.name] = {urlPath: plugin.route};
  Logger.log(
    `Registered plugin (name: ${plugin.name}, address: ${plugin.address}) on the route "${plugin.route}".`,
  );

  return {success: true};
}

/**
 * Removes a plugin from the list (map) of registered plugins using the provided route
 * @param route Route of the plugin which needs to be unregistered
 */
function removePlugin(route: string): SCPluginRegisterResponse {
  if (!plugins.has(route)) {
    throw new SCNotFoundErrorResponse(isTestEnvironment);
  }
  if (plugins.has(route)) {
    const plugin = plugins.get(route)!;
    delete backendConfig.app.features.plugins?.[plugin.name];
  }
  // remove the plugin information using its route as a key
  plugins.delete(route);
  Logger.log(`Removed plugin that used the route "${route}".`);

  return {success: true};
}
