import express from 'express';
import bodyParser from 'body-parser';

import ModelFileModel from '../models/model_file';
import ModelModel from '../models/model';
import _ from 'lodash';
import ModelValidate from './validate/model';
import res400 from './validate/res400';
import db from '../db';

const router = express.Router();

router.get('/model/groups', (req, res, next) => {
  if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;

  ModelModel.find().lean().exec((err, data) => {
    if (err) return next(err);

    // validate all and filter null values from validation errors
    res.json(_.compact(data.map(e => ModelValidate.output(e))));
  });
});

router.post('/model/:group', (req, res, next) => {
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;

  const {error, value: model} = ModelValidate.input(req.body);
  console.log(error);
  if (error) return res400(error, res);

  ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
    if (err) return next(err);

    if (data) {  // group exists
      if (data.models.find(e => e.name === model.name)) {  // name exists, overwrite
        ModelModel.findOneAndUpdate(
          {$and: [{group: req.params.group}, {'models.name': model.name}]},
          {'models.$': model},
          {upsert: true}).log(req).lean().exec(err => {
          if (err) return next(err);
          res.json({status: 'OK'})
        });
      }
      else {  // create new
        ModelModel.findOneAndUpdate(
          {group: req.params.group},
          {$push: {models: model as never}}
        ).log(req).lean().exec(err => {
          if (err) return next(err);
          res.json({status: 'OK'})
        });
      }
    }
    else {  // create new group
      new ModelModel({group: req.params.group, models: [model]}).save((err, data) => {
        if (err) return next(err);
        db.log(req, 'models', {_id: data._id}, data.toObject());
        res.json({status: 'OK'})
      });
    }
  });
});

router.delete('/model/:group(((?!file)[^\\/]+?))/:name', (req, res, next) => {
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;

  ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
    if (err) return next(err);

    if (!data || !data.models.find(e => e.name === req.params.name)) {
      return res.status(404).json({status: 'Not found'});
    }
    if (data.models.length > 1) {  // only remove model
      ModelModel.findOneAndUpdate(
        {group: req.params.group},
        {$pull: {models: data.models.find(e => e.name === req.params.name) as never}}
      ).log(req).lean().exec(err => {
        if (err) return next(err);
        res.json({status: 'OK'})
      });
    }
    else {  // remove document
      ModelModel.findOneAndDelete({group: req.params.group}).log(req).lean().exec(err => {
        if (err) return next(err);
        res.json({status: 'OK'})
      });
    }
  });
});

router.get('/model/file/:name', (req, res, next) => {
  if (!req.auth(res, ['dev', 'admin'], 'all')) return;

  ModelFileModel.findOne({name: req.params.name}).lean().exec((err, data) => {
    if (err) return next(err);
    if (data) {
      res.set('Content-Type', 'application/octet-stream');
      res.send(data.data.buffer);
    }
    else {
      res.status(404).json({status: 'Not found'});
    }
  });
});

router.post('/model/file/:name', bodyParser.raw({limit: '5mb'}), (req, res, next) => {
  if (!req.auth(res, ['dev', 'admin'], 'all')) return;

  ModelFileModel.replaceOne({name: req.params.name}, {name: req.params.name, data: req.body}).setOptions({upsert: true})
    .lean().exec(err => {
    if (err) return next(err);
    res.json({status: 'OK'});
  });
});

router.delete('/model/file/:name', (req, res, next) => {
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;

  ModelFileModel.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
    if (err) return next(err);
    if (data) {
      res.json({status: 'OK'});
    }
    else {
      res.status(404).json({status: 'Not found'});
    }
  });
});

module.exports = router;