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

'use strict';

const slug = require('slug');

const mongoose = require('mongoose');

const Sticker = mongoose.model('Sticker');
const User = mongoose.model('User');

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

const MAX_CHANNEL_STICKERS = 50;
const MAX_USER_STICKERS = 10;

class StickerService extends SiteService {

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

  async start ( ) {
    await super.start();

    const { user: userService } = this.dtp.services;
    this.populateSticker = [
      {
        path: 'owner',
        select: userService.USER_SELECT,
      },
    ];

    this.queue = this.getJobQueue('media', this.dtp.config.jobQueues.media);
    this.stickerTemplate = this.loadViewTemplate('sticker/components/sticker-standalone.pug');
  }

  async createSticker (ownerType, owner, file, stickerDefinition) {
    const { minio: minioService } = this.dtp.services;
    const NOW = new Date();

    this.log.debug('received sticker', { file, stickerDefinition });

    // this is faster than Model.countDocuments()
    const currentStickers = await Sticker.find({ owner: owner._id }).select('_id').lean();

    switch (ownerType) {
      case 'ChatRoom':
        if (currentStickers.length >= MAX_CHANNEL_STICKERS) {
          throw new SiteError(508, `You have ${MAX_CHANNEL_STICKERS} stickers. Please remove a sticker before adding a new one.`);
        }
        break;

      case 'User':
        if (currentStickers.length >= MAX_USER_STICKERS) {
          throw new SiteError(508, `You have ${MAX_USER_STICKERS} stickers. Please remove a sticker before adding a new one one.`);
        }
        break;
    }

    const sticker = new Sticker();
    sticker.created = NOW;
    sticker.status = 'processing';
    sticker.ownerType = ownerType;
    sticker.owner = owner._id;
    sticker.slug = slug(stickerDefinition.slug.toLowerCase().trim());

    const bucket = process.env.MINIO_VIDEO_BUCKET;
    const key = `/stickers/${sticker._id.toString().slice(0, 3)}/${sticker._id}`;

    sticker.original = {
      bucket, key,
      type: file.mimetype,
      size: file.size,
    };

    await minioService.uploadFile({
      bucket, key,
      filePath: file.path,
      metadata: {
        'Content-Type': file.mimetype,
        'Content-Length': file.size,
      },
    });

    await sticker.save();

    await this.queue.add('sticker-ingest', { stickerId: sticker._id });

    return sticker.toObject();
  }

  async getForChannel (channel) {
    const stickers = await Sticker
      .find({ status: 'live', ownerType: 'Channel', owner: channel._id })
      .sort({ created: -1 })
      .populate(this.populateSticker)
      .lean();
    return stickers;
  }

  async getForUser (user) {
    const stickers = await Sticker
      .find({ status: 'live', ownerType: 'User', owner: user._id })
      .sort({ created: -1 })
      .populate(this.populateSticker)
      .lean();
    return stickers;
  }

  async getStickers (pagination) {
    const stickers = await Sticker
      .find()
      .sort({ created: -1 })
      .skip(pagination.skip)
      .limit(pagination.cpp)
      .populate(this.populateSticker)
      .lean();
    return stickers;
  }

  async getBySlug (slug) {
    const sticker = await Sticker
      .findOne({ slug })
      .populate(this.populateSticker)
      .lean();
    return sticker;
  }

  async getById (stickerId, includeOriginal = false) {
    let query = Sticker.findOne({ _id: stickerId });
    if (includeOriginal) {
      query = query.select('+original');
    }
    const sticker = await query
      .populate(this.populateSticker)
      .lean();
    return sticker;
  }

  async setStatus (sticker, status) {
    await Sticker.updateOne({ _id: sticker._id }, { $set: { status } });
  }

  async resolveStickerSlugs (slugs) {
    const stickers = [ ];
    await SiteAsync.each(slugs, async (slug) => {
      const sticker = await Sticker.findOne({ slug: slug });
      if (!sticker) {
        return;
      }
      stickers.push(sticker);
    });
    return stickers;
  }

  async removeSticker (sticker) {
    const stickerId = sticker._id;
    this.log.info('creating sticker delete job', { stickerId });
    await this.queue.add('sticker-delete', { stickerId });
  }

  async render (sticker, stickerOptions) {
    return this.stickerTemplate({ sticker, stickerOptions });
  }

  async addFavorite (user, stickerId) {
    stickerId = mongoose.Types.ObjectId(stickerId);
    const sticker = await Sticker.findById(stickerId);
    if (!sticker) {
      throw new SiteError(404, 'Sticker not found');
    }
    await User.updateOne(
      { _id: user._id },
      {
        $addToSet: { favoriteStickers: sticker._id },
      },
    );
  }

  async removeFavorite (user, stickerId) {
    stickerId = mongoose.Types.ObjectId(stickerId);
    await User.updateOne(
      { _id: user._id },
      {
        $pull: { favoriteStickers: stickerId },
      },
    );
  }

  async getFavorites (user) {
    const stickers = await Sticker
      .populate(user.favoriteStickers, this.populateSticker);
    return stickers;
  }
}

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