import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js'
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
import { assertUserHasPaidBenefits } from '/imports/api/users/patreon/tiers.js';

import '/imports/api/creature/removeCreature.js';
import '/imports/api/creature/restCreature.js';

//set up the collection for creatures
let Creatures = new Mongo.Collection('creatures');

let CreatureSettingsSchema = new SimpleSchema({
	//slowed down by carrying too much?
	useVariantEncumbrance: {
		type: Boolean,
		optional: true,
	},
	//hide spellcasting tab
	hideSpellcasting: {
		type: Boolean,
		optional: true,
	},
	// Swap around the modifier and stat
	swapStatAndModifier: {
		type: Boolean,
		optional: true,
	},
  // Hide all the unused stats
  hideUnusedStats: {
    type: Boolean,
    optional: true,
  },
  // How much each hitDice resets on a long rest
  hitDiceResetMultiplier: {
    type: Number,
    optional: true,
    min: 0,
    max: 1,
  },
  discordWebhook: {
    type: String,
    optional: true,
    max: 200,
  },
});

let CreatureSchema = new SimpleSchema({
	// Strings
	name: {
		type: String,
		defaultValue: '',
		optional: true,
	},
	alignment: {
		type: String,
		optional: true
	},
	gender: {
		type: String,
		optional: true
	},
	picture: {
		type: String,
		optional: true
	},
  avatarPicture: {
    type: String,
    optional: true,
  },
	// Mechanics
	deathSave: {
		type: deathSaveSchema,
		defaultValue: {},
	},
  // Stats that are computed and denormalised outside of recomputation
  denormalizedStats: {
    type: Object,
    defaultValue: {},
  },
  // Sum of all XP gained by this character
	'denormalizedStats.xp': {
		type: SimpleSchema.Integer,
		defaultValue: 0,
	},
  // Sum of all levels granted by milestone XP
  'denormalizedStats.milestoneLevels': {
    type: SimpleSchema.Integer,
    defaultValue: 0,
  },
  // Inventory
	'denormalizedStats.weightTotal': {
		type: Number,
		defaultValue: 0,
	},
  'denormalizedStats.weightEquipment': {
		type: Number,
		defaultValue: 0,
	},
  'denormalizedStats.weightCarried': {
		type: Number,
		defaultValue: 0,
	},
  'denormalizedStats.valueTotal': {
		type: Number,
		defaultValue: 0,
	},
  'denormalizedStats.valueEquipment': {
		type: Number,
		defaultValue: 0,
	},
  'denormalizedStats.valueCarried': {
		type: Number,
		defaultValue: 0,
	},
  'denormalizedStats.itemsAttuned': {
		type: Number,
		defaultValue: 0,
	},
  // Version of computation engine that was last used to compute this creature
  computeVersion: {
		type: String,
    optional: true,
	},
	type: {
		type: String,
		defaultValue: 'pc',
		allowedValues: ['pc', 'npc', 'monster'],
	},
  damageMultipliers: {
    type: Object,
		blackbox: true,
		defaultValue: {}
  },
	variables: {
		type: Object,
		blackbox: true,
		defaultValue: {}
	},

  // Tabletop
  tabletop: {
    type: String,
    regEx: SimpleSchema.RegEx.id,
    optional: true,
  },
  initiativeRoll: {
    type: SimpleSchema.Integer,
    optional: true,
  },

	// Settings
	settings: {
		type: CreatureSettingsSchema,
		defaultValue: {},
	},
});

CreatureSchema.extend(ColorSchema);
CreatureSchema.extend(SharingSchema);

Creatures.attachSchema(CreatureSchema);

const insertCreature = new ValidatedMethod({

  name: 'creatures.insertCreature',

  validate: null,

  mixins: [RateLimiterMixin],
  rateLimit: {
    numRequests: 5,
    timeInterval: 5000,
  },

  run() {
    if (!this.userId) {
      throw new Meteor.Error('Creatures.methods.insert.denied',
      'You need to be logged in to insert a creature');
    }
    assertUserHasPaidBenefits(this.userId);

		// Create the creature document
    let creatureId = Creatures.insert({
			owner: this.userId,
		});
    CreatureProperties.insert({
      slotTags: ['base'],
      quantityExpected: 1,
      type: 'propertySlot',
      name: 'Base',
      description: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base, your sheet will be empty.',
      hideWhenFull: true,
      parent: {collection: 'creatures', id: creatureId},
      ancestors: [{collection: 'creatures', id: creatureId}],
      order: 0,
      tags: [],
      spaceLeft: 1,
      totalFilled: 0,
    });
		this.unblock();
		return creatureId;
  },

});

const updateCreature = new ValidatedMethod({
  name: 'creatures.update',
  validate({_id, path}){
		if (!_id) return false;
		// Allowed fields
		let allowedFields = [
      'name',
      'alignment',
      'gender',
      'picture',
      'avatarPicture',
      'color',
      'settings',
    ];
		if (!allowedFields.includes(path[0])){
			throw new Meteor.Error('Creatures.methods.update.denied',
      'This field can\'t be updated using this method');
		}
  },
  mixins: [RateLimiterMixin],
  rateLimit: {
    numRequests: 5,
    timeInterval: 5000,
  },
  run({_id, path, value}) {
		let creature = Creatures.findOne(_id);
    assertEditPermission(creature, this.userId);
    if (value === undefined || value === null){
      Creatures.update(_id, {
        $unset: {[path.join('.')]: 1},
      });
    } else {
      Creatures.update(_id, {
        $set: {[path.join('.')]: value},
      });
    }
  },
});

export default Creatures;
export { CreatureSchema, insertCreature, updateCreature };
