// admin/user.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

const express = require('express');

const { SiteController, SiteError } = require('../../../lib/site-lib');

class AdminUserController extends SiteController {

  constructor (dtp) {
    super(dtp, module.exports);
  }

  async start ( ) {
    const { jobQueue: jobQueueService } = this.dtp.services;

    this.jobQueues = { };
    this.jobQueues.reeeper = await jobQueueService.getJobQueue(
      'reeeper',
      this.dtp.config.jobQueues.reeeper,
    );

    const router = express.Router();
    router.use(async (req, res, next) => {
      res.locals.currentView = 'admin';
      res.locals.adminView = 'user';
      return next();
    });

    router.param('localUserId', this.populateLocalUserId.bind(this));
    router.param('archiveJobId', this.populateArchiveJobId.bind(this));
    router.param('archiveId', this.populateArchiveId.bind(this));

    router.post('/local/:localUserId/archive', this.postArchiveLocalUser.bind(this));
    router.post('/local/:localUserId', this.postUpdateLocalUser.bind(this));

    router.get('/local/:localUserId/archive/confirm', this.getArchiveLocalUserConfirm.bind(this));
    router.get('/local/:localUserId', this.getLocalUserView.bind(this));

    router.get('/archive/job/:archiveJobId', this.getUserArchiveJobView.bind(this));

    router.post('/archive/:archiveId/action', this.postArchiveAction.bind(this));
    router.get('/archive/:archiveId/file', this.getUserArchiveFile.bind(this));
    router.get('/archive/:archiveId', this.getUserArchiveView.bind(this));

    router.get('/archive', this.getUserArchiveIndex.bind(this));

    router.get('/', this.getHomeView.bind(this));


    return router;
  }

  async populateLocalUserId (req, res, next, localUserId) {
    const { user: userService } = this.dtp.services;
    try {
      res.locals.userAccount = await userService.getLocalUserAccount(localUserId);
      if (!res.locals.userAccount) {
        throw new SiteError(404, 'User not found');
      }
      return next();
    } catch (error) {
      return next(error);
    }
  }

  async populateArchiveJobId (req, res, next, archiveJobId) {
    try {
      res.locals.job = await this.jobQueues.reeeper.getJob(archiveJobId);
      if (!res.locals.job) {
        throw new SiteError(404, 'Job not found');
      }
      return next();
    } catch (error) {
      this.log.error('failed to populate Bull queue job', { archiveJobId, error });
      return next(error);
    }
  }

  async populateArchiveId (req, res, next, archiveId) {
    const { user: userService } = this.dtp.services;
    try {
      res.locals.archive = await userService.getArchiveById(archiveId);
      if (!res.locals.archive) {
        throw new SiteError(404, 'Archive not found');
      }
      return next();
    } catch (error) {
      this.log.error('failed to populate UserArchive', { archiveId, error });
      return next(error);
    }
  }

  async postArchiveLocalUser (req, res, next) {
    const {
      logan: loganService,
      user: userService,
    } = this.dtp.services;
    try {
      
      const user = await userService.getLocalUserAccount(req.body.userId);
      if (!user) {
        throw new SiteError(404, 'User not found');
      }
      if (req.user && req.user._id.equals(user._id)) {
        throw new SiteError(400, "You can't archive yourself");
      }
      res.locals.job = await userService.archiveLocalUser(user);
      loganService.sendRequestEvent(module.exports, req, {
        level: 'info',
        event: 'postArchiveUser',
        data: {
          job: res.locals.job.id,
          user: user,
        },
      });
      res.redirect(`/admin/user/archive/job/${res.locals.job.id}`);
    } catch (error) {
      loganService.sendRequestEvent(module.exports, req, {
        level: 'error',
        event: 'postArchiveUser',
        data: {
          offender: {
            _id: req.body.userId,
          },
          error,
        },
      });
      return next(error);
    }
  }

  async postUpdateLocalUser (req, res, next) {
    const {
      logan: loganService,
      user: userService,
    } = this.dtp.services;
    try {
      this.log.debug('local user update', { action: req.body.action });
      switch (req.body.action) {
        case 'update':
          if (req.user._id.equals(res.locals.userAccount._id)) {
             if (req.user.flags.isAdmin && (req.body.isAdmin !== 'on')) {
               throw new SiteError(400, "You can't remove your own admin privileges");
             }
          }
          await userService.updateLocalForAdmin(res.locals.userAccount, req.body);
          loganService.sendRequestEvent(module.exports, req, {
            level: 'info',
            event: 'postUpdateLocalUser',
            message: 'local user account updated',
            data: {
              userAccount: {
                _id: res.locals.userAccount._id,
                username: res.locals.userAccount.username,
              },
            },
          });
          break;

        case 'ban':
          if (req.user._id.equals(res.locals.userAccount._id)) {
             throw new SiteError(400, "You can't ban yourself");
          }
          await userService.ban(res.locals.userAccount);
          loganService.sendRequestEvent(module.exports, req, {
            level: 'info',
            event: 'postUpdateLocalUser',
            message: 'local user banned from the app',
            data: {
              userAccount: {
                _id: res.locals.userAccount._id,
                username: res.locals.userAccount.username,
              },
            },
          });
          break;
      }
      res.redirect('/admin/user');
    } catch (error) {
      return next(error);
    }
  }

  async getLocalUserView (req, res, next) {
    const { comment: commentService } = this.dtp.services;
    try {
      res.locals.pagination = this.getPaginationParameters(req, 20);
      res.locals.recentComments = await commentService.getForAuthor(res.locals.userAccount, res.locals.pagination);
      res.render('admin/user/form');
    } catch (error) {
      this.log.error('failed to produce user view', { error });
      return next(error);
    }
  }

  async getUserArchiveJobView (req, res) {
    res.locals.adminView = 'user-archive';
    res.render('admin/user/archive/job');
  }

  async getArchiveLocalUserConfirm (req, res) {
    res.locals.adminView = 'user-archive';
    res.render('admin/user/archive/confirm');
  }

  async postArchiveAction (req, res, next) {
    const {
      logan: loganService,
      user: userService,
    } = this.dtp.services;
    try {
      switch (req.body.action) {
        case 'update':
          loganService.sendRequestEvent(module.exports, req, {
            level: 'info',
            event: 'postArchiveAction',
            message: 'updating user archive record',
            data: {
              archive: {
                _id: res.locals.archive._id,
                user: {
                  _id: res.locals.archive.user._id,
                  username: res.locals.archive.user.username,
                },
              },
            },
          });
          await userService.updateArchive(res.locals.archive, req.body);
          return res.redirect(`/admin/user/archive/${res.locals.archive._id}`);

        case 'delete-file':
          loganService.sendRequestEvent(module.exports, req, {
            level: 'info',
            event: 'postArchiveAction',
            message: 'removing user archive file',
            data: {
              archive: {
                _id: res.locals.archive._id,
                user: {
                  _id: res.locals.archive.user._id,
                  username: res.locals.archive.user.username,
                },
              },
            },
          });
          await userService.deleteArchiveFile(res.locals.archive);
          return res.redirect(`/admin/user/archive/${res.locals.archive._id}`);

        case 'delete':
          loganService.sendRequestEvent(module.exports, req, {
            level: 'info',
            event: 'postArchiveAction',
            message: 'removing user archive',
            data: {
              archive: {
                _id: res.locals.archive._id,
                user: {
                  _id: res.locals.archive.user._id,
                  username: res.locals.archive.user.username,
                },
              },
            },
          });
          await userService.deleteArchive(res.locals.archive);
          return res.redirect(`/admin/user/archive`);

        default:
          // unknown/invalid action
          break;
      }

      throw new SiteError(400, `Invalid user archive action: ${req.body.action}`);

    } catch (error) {
      this.log.error('failed to delete archive file', { error });
      return next(error);
    }
  }

  async getUserArchiveFile (req, res, next) {
    const { minio: minioService } = this.dtp.services;
    try {
      res.locals.adminView = 'user-archive';

      this.log.debug('archive', { archive: res.locals.archive });
      const stream = await minioService.openDownloadStream({
        bucket: res.locals.archive.archive.bucket,
        key: res.locals.archive.archive.key,
      });

      res.status(200);
      res.set('Content-Type', 'application/zip');
      res.set('Content-Size', res.locals.archive.archive.size);
      res.set('Content-Disposition', `attachment; filename="user-${res.locals.archive.user._id}.zip"`);

      stream.pipe(res);
    } catch (error) {
      this.log.error('failed to stream user archive file', { error });
      return next(error);
    }
  }

  async getUserArchiveView (req, res) {
    res.locals.adminView = 'user-archive';
    res.render('admin/user/archive/view');
  }

  async getUserArchiveIndex (req, res, next) {
    const { user: userService } = this.dtp.services;
    try {
      res.locals.adminView = 'user-archive';

      res.locals.pagination = this.getPaginationParameters(req, 20);
      res.locals.archive = await userService.getArchives(res.locals.pagination);

      res.render('admin/user/archive/index');
    } catch (error) {
      this.log.error('failed to render the User archives index', { error });
      return next(error);
    }
  }

  async getHomeView (req, res, next) {
    const { user: userService } = this.dtp.services;
    try {
      res.locals.pagination = this.getPaginationParameters(req, 10);
      res.locals.userAccounts = await userService.searchLocalUserAccounts(res.locals.pagination, req.query.u);
      res.locals.totalUserCount = await userService.getTotalCount();
      res.render('admin/user/index');
    } catch (error) {
      return next(error);
    }
  }
}

module.exports = {
  logId: 'ctl:admin:user',
  index: 'adminUser',
  className: 'AdminUserController',
  create: async (dtp) => { return new AdminUserController(dtp); },
};