// Import libs
import express from 'express';

// Import shared types
import User from '../shared/types/User';

// Import shared helpers
import currentUser from '../currentUser';
import genInstallXML from './genInstallXML';
import CanvasExternalTool from 'caccl-api/lib/types/CanvasExternalTool';

// Import shared constants
import TEST_INSTALL_CREDS from '../shared/constants/TEST_INSTALL_CREDS';

/* eslint-disable no-console */

/**
 * Initializes simulated LTI launch functionality
 * @author Gabe Abrams
 * @async
 * @param {object} opts object containing all args
 * @param {Express.Application} opts.app the express app to add routes to
 * @param {string} opts.appName the name of the app
 * @param {number} opts.courseId the id of the test Canvas course
 * @param {User} opts.teacher an instructor
 * @param {User[]} opts.tas a list of TAs
 * @param {User[]} opts.students a list of students
 * @param {object} [opts.customLaunchPaths] map of custom parameters
 */
const initLaunches = async (
  opts: {
    app: express.Application,
    appName: string,
    courseId: number,
    teacher: User,
    tas: User[],
    students: User[],
    customParams?: { [k: string]: string },
    customLaunchPaths?: {
      name: string,
      path: string,
    }[],
  },
) => {
  const {
    app,
    appName,
    courseId,
    teacher,
    tas,
    students,
    customParams = {},
    customLaunchPaths = [],
  } = opts;

  /* --------------------------- Routes --------------------------- */

  /**
   * Simulator homepage
   * @author Gabe Abrams
   */
  app.get(
    '/simulator',
    async (req, res) => {
      // Render the launch page
      return res.render(
        `${__dirname}/launchPage`,
        {
          teacher,
          tas,
          students,
          isSelfLaunch: (
            req.query.isSelfLaunch
            && req.query.isSelfLaunch === 'true'
          ),
          customLaunchPaths,
        }
      );
    },
  );

  /**
   * Redirect to launch
   * @author Gabe Abrams
   * @param {number} userId user's Canvas id
   */
  app.get(
    '/simulator/users/:userId/launch',
    async (req, res) => {
      // Get the user's id
      const id = Number.parseInt(req.params.userId);

      // Save the current user
      currentUser.set(id);

      // Get current user's API
      const user = currentUser.get();
      if (!user || !user.api) {
        return res.send('Oops! We could not launch as the user you selected. We could not find their credentials.');
      }
      const { api } = user;

      // Create a unique app name
      const uniqueAppName = `CACCL Test App [${appName}] [${(new Date()).toLocaleDateString()}]`;

      // Generate an install xml
      const xml = genInstallXML({
        appName: uniqueAppName,
        customParams,
      });

      // Create a dummy app
      let testApp: CanvasExternalTool;
      try {
        testApp = await teacher.api.course.app.addByXML({
          courseId,
          name: uniqueAppName,
          key: TEST_INSTALL_CREDS.key,
          secret: TEST_INSTALL_CREDS.secret,
          xml,
        });
      } catch (err) {
        return res.send(`Oops! We could not launch as the user you selected. We could not create a test app in your sandbox course because an error occurred: ${err.message}`);
      }

      // Simulate a nav launch
      try {
        const sessionlessLaunchURL = await api.course.app.getNavLaunchURL({
          courseId,
          appId: testApp.id,
        });

        // Send user to the launch URL
        res.redirect(sessionlessLaunchURL);
      } catch (err) {
        return res.send(`Oops! We ran into an issue while asking Canvas for a launch URL: ${err.message}`);
      }

      // Wait and then uninstall the test app
      await new Promise((r) => {
        setTimeout(r, 10000);
      });
      try {
        await teacher.api.course.app.remove({
          courseId,
          appId: testApp.id,
        });
      } catch (err) {
        console.log('An error occurred when trying to clean up a test app:');
        console.log(err);
      }
    },
  );
};

export default initLaunches;
