import express from 'express';
import mongoose from 'mongoose';
import _ from 'lodash';

import ConditionValidate from './validate/condition';
import ParametersValidate from './validate/parameters';
import res400 from './validate/res400';
import SampleModel from '../models/sample';
import ConditionModel from '../models/condition';
import TreatmentTemplateModel from '../models/treatment_template';
import IdValidate from './validate/id';


const router = express.Router();

router.get('/condition/' + IdValidate.parameter(), (req, res, next) => {
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;

  ConditionModel.findById(req.params.id).lean().exec((err, data) => {
    if (err) return next(err);
    if (data) {
      res.json(ConditionValidate.output(data));
    }
    else {
      res.status(404).json({status: 'Not found'});
    }
  });
});

router.put('/condition/' + IdValidate.parameter(), async (req, res, next) => {
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;

  const {error, value: condition} = ConditionValidate.input(req.body, 'change');
  if (error) return res400(error, res);

  const data = await ConditionModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
  if (data instanceof Error) return;
  if (!data) {
    res.status(404).json({status: 'Not found'});
  }
  // add properties needed for sampleIdCheck
  condition.treatment_template = data.treatment_template;
  condition.sample_id = data.sample_id;
  if (!await sampleIdCheck(condition, req, res, next)) return;
  if (condition.parameters) {
    condition.parameters = _.assign({}, data.parameters, condition.parameters);
    if (!_.isEqual(condition.parameters, data.parameters)) {
      condition.status = 0;
    }
  }
  if (!await treatmentCheck(condition, 'change', res, next)) return;

  await ConditionModel.findByIdAndUpdate(req.params.id, condition, {new: true}).lean().exec((err, data) => {
    if (err) return next(err);
    res.json(ConditionValidate.output(data));
  });
});

router.delete('/condition/' + IdValidate.parameter(), (req, res, next) => {
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;

  ConditionModel.findById(req.params.id).lean().exec(async (err, data: any) => {
    if (err) return next(err);
    if (!data) {
      res.status(404).json({status: 'Not found'});
    }
    if (!await sampleIdCheck(data, req, res, next)) return;
    await ConditionModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => {
      if (err) return next(err);
      res.json({status: 'OK'});
    });
  });
});

router.post('/condition/new', async (req, res, next) => {
  if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;

  const {error, value: condition} = ConditionValidate.input(req.body, 'new');
  if (error) return res400(error, res);

  if (!await sampleIdCheck(condition, req, res, next)) return;
  if (!await numberCheck(condition, res, next)) return;
  if (!await treatmentCheck(condition, 'new', res, next)) return;

  condition.status = 0;
  await new ConditionModel(condition).save((err, data) => {
    if (err) return next(err);
    res.json(ConditionValidate.output(data.toObject()));
  });
})


module.exports = router;


async function sampleIdCheck (condition, req, res, next) {  // validate sample_id, returns false if invalid
  const sampleData = await SampleModel.findById(condition.sample_id).lean().exec().catch(err => {next(err); return false;}) as any;
  if (!sampleData) {  // sample_id not found
    res.status(400).json({status: 'Sample id not available'});
    return false
  }

  if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return false;  // sample does not belong to user
  return true;
}

async function numberCheck (condition, res, next) {  // validate number, returns false if invalid
  const data = await ConditionModel.find({sample_id: new mongoose.Types.ObjectId(condition.sample_id), number: condition.number}).lean().exec().catch(err => {next(err); return false;}) as any;
  if (data.length) {
    res.status(400).json({status: 'Condition number already taken'});
    return false;
  }
  return true;
}

async function treatmentCheck (condition, param, res, next) {
  const treatmentData = await TreatmentTemplateModel.findById(condition.treatment_template).lean().exec().catch(err => {next(err); return false;}) as any;
  if (!treatmentData) {  // template not found
    res.status(400).json({status: 'Treatment template not available'});
    return false
  }

  // validate parameters
  const {error, value: ignore} = ParametersValidate.input(condition.parameters, treatmentData.parameters, param);
  if (error) {res400(error, res); return false;}
  return true;
}