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

'use strict';

const striptags = require('striptags');
const slug = require('slug');

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

const mongoose = require('mongoose');
const ObjectId = mongoose.Types.ObjectId;

const Page = mongoose.model('Page');

class PageService extends SiteService {

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

  async menuMiddleware (req, res, next) {
    try {
      const pages = await Page
        .find({ status: 'published', parent: { $exists: false } })
        .select('slug menu')
        .lean();

      res.locals.mainMenu = pages
        .filter((page) => !page.parent)
        .map((page) => {
          return {
            url: `/page/${page.slug}`,
            slug: page.slug,
            icon: page.menu.icon,
            label: page.menu.label,
            order: page.menu.order,
          };
        })
        .sort((a, b) => {
          if (a.order < b.order) {
            return a;
          }
          return b;
        });

      return next();
    } catch (error) {
      this.log.error('failed to build page menu', { error });
      return next();
    }
  }

  async createPlaceholder (author) {
    const NOW = new Date();

    if (!author.flags.isAdmin) {
      throw new SiteError(403, 'You are not permitted to author pages');
    }

    let page = new Page();
    page.created = NOW;
    page.authorType = author.type;
    page.author = author._id;
    page.title = "New Draft page";
    page.slug = `draft-page-${page._id}`;
    await page.save();

    page = page.toObject();
    page.author = author; // self-populate instead of calling db

    return page;
  }

  async create (author, pageDefinition) {
    if (!author.permissions.canAuthorPages) {
      throw new SiteError(403, 'You are not permitted to author pages');
    }

    const page = new Page();
    page.title = striptags(pageDefinition.title.trim());
    page.slug = this.createPageSlug(page._id, page.title);
    page.content = pageDefinition.content.trim();
    page.status = pageDefinition.status || 'draft';

    page.menu = {
      icon: striptags((pageDefinition.menuIcon || 'fa-slash').trim().toLowerCase()),
      label: striptags((pageDefinition.menuLabel || page.title.slice(0, 10))),
      order: parseInt(pageDefinition.menuOrder || '0', 10),
    };

    if (pageDefinition.parentPageId && (pageDefinition.parentPageId !== 'none')) {
      page.menu.parent = pageDefinition.parentPageId;
    }
    await page.save();

    return page.toObject();
  }

  async update (user, page, pageDefinition) {
    const NOW = new Date();
    const updateOp = {
      $set: {
        updated: NOW,
      },
    };

    // if (!user.permissions.canAuthorPages) {
    //   throw new SiteError(403, 'You are not permitted to author or change pages.');
    // }

    if (pageDefinition.title) {
      updateOp.$set.title = striptags(pageDefinition.title.trim());
    }
    if (pageDefinition.slug) {
      let pageSlug = striptags(slug(pageDefinition.slug.trim())).split('-');
      while (ObjectId.isValid(pageSlug[pageSlug.length - 1])) {
        pageSlug.pop();
      }
      pageSlug = pageSlug.splice(0, 4);
      pageSlug.push(page._id.toString());
      updateOp.$set.slug = `${pageSlug.join('-')}`;
    }
    if (pageDefinition.summary) {
      updateOp.$set.summary = striptags(pageDefinition.summary.trim());
    }
    if (pageDefinition.content) {
      updateOp.$set.content = pageDefinition.content.trim();
    }

    updateOp.$set.menu = {
      icon: striptags((pageDefinition.menuIcon || 'fa-slash').trim().toLowerCase()),
      label: striptags((pageDefinition.menuLabel || page.title.slice(0, 10))),
      order: parseInt(pageDefinition.menuOrder || '0', 10),
    };

    if (pageDefinition.parentPageId && (pageDefinition.parentPageId !== 'none')) {
      updateOp.$set.menu.parent = pageDefinition.parentPageId;
    }

    // if old status is not published and new status is published, we have to
    // verify that the calling user has Publisher privileges.
    if ((page.status !== 'published') && (pageDefinition.status === 'published')) {
      if (!user.permissions.canPublishPages) {
        throw new SiteError(403, 'You are not permitted to publish pages');
      }
      updateOp.$set.status = striptags(pageDefinition.status.trim());
    }

    await Page.updateOne(
      { _id: page._id },
      updateOp,
      { upsert: true },
    );
  }

  async getPages (pagination, status = ['published']) {
    if (!Array.isArray(status)) {
      status = [status];
    }
    const pages = await Page
      .find({ status: { $in: status } })
      .sort({ created: -1 })
      .skip(pagination.skip)
      .limit(pagination.cpp)
      .lean();
    return pages;
  }

  async getById (pageId) {
    const page = await Page
      .findById(pageId)
      .select('+content')
      .lean();
    return page;
  }

  async getBySlug (pageSlug) {
    const slugParts = pageSlug.split('-');
    const pageId = slugParts[slugParts.length - 1];
    return this.getById(pageId);
  }

  async getAvailablePages (excludedPageIds) {
    const search = { };
    if (excludedPageIds) {
      search._id = { $nin: excludedPageIds };
    }
    const pages = await Page.find(search).lean();
    return pages;
  }

  async deletePage (page) {
    this.log.info('deleting page', { pageId: page._id });
    await Page.deleteOne({ _id: page._id });
  }

  createPageSlug (pageId, pageTitle) {
    if ((typeof pageTitle !== 'string') || (pageTitle.length < 1)) {
      throw new Error('Invalid input for making a page slug');
    }
    const pageSlug = slug(pageTitle.trim().toLowerCase()).split('-').slice(0, 4).join('-');
    return `${pageSlug}-${pageId}`;
  }
}

module.exports = {
  slug: 'page',
  name: 'page',
  create: (dtp) => { return new PageService(dtp); },
};
