import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
import dealDamage from '/imports/api/creature/creatureProperties/methods/dealDamage.js';
import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
import { CompilationContext } from '/imports/parser/parser.js';

export default function applyDamage({
  prop,
  creature,
  targets,
  actionContext,
  log,
}){
  let damageTargets = prop.target === 'self' ? [creature] : targets;
  let scope = {
    ...creature.variables,
    ...actionContext,
  };
  // Add the target's variables to the scope
  if (targets.length === 1){
    scope.target = targets[0].variables;
  }
  // Determine if the hit is critical
  let criticalHit = !!(
    actionContext.criticalHit &&
    actionContext.criticalHit.value &&
    prop.damageType !== 'healing' // Can't critically heal
  );
  // Double the damage rolls if the hit is critical
  let context = new CompilationContext({
    doubleRolls: criticalHit,
  });

  // Compute the roll the first time, logging any errors
  var {result} = evaluateString({
    string: prop.amount,
    scope,
    fn: 'reduce',
    context
  });

  // If the result is an error bail out now
  if (result.constructor.name === 'ErrorNode'){
    log.content.push({
      name: 'Damage error',
      error: result.toString(),
    });
    return;
  }

  // Memoise the damage suffix for the log
  let suffix = (criticalHit ? ' critical ' : '') +
    prop.damageType +
    (prop.damageType !== 'healing' ? ' damage': '');

  if (damageTargets && damageTargets.length) {
    // Iterate through all the targets
    damageTargets.forEach(target => {
      let name = prop.damageType === 'healing' ? 'Healing' : 'Damage';

      // Reroll the damage if needed
      if (prop.target === 'each'){
        ({result, context} = evaluateString({
          string: prop.amount,
          scope,
          fn: 'reduce'
        }));
      }
      // If the result is an error or not a number bail out now
      if (result.constructor.name === 'ErrorNode' || !result.isNumber){
        log.content.push({
          name: 'Damage error',
          error: result.toString(),
        });
        return;
      }

      // Deal the damage to the target
      let damageDealt = dealDamage.call({
        creatureId: target._id,
        damageType: prop.damageType,
        amount: result.value,
      });

      // Log the damage done
      if (target._id === creature._id){
        // Target is same as self, log damage as such
        log.content.push({
          name,
          result: damageDealt,
          details: suffix + ' to self',
        });
      } else {
        log.content.push({
          name,
          resultPrefix: 'Dealt ',
          result: damageDealt,
          details: suffix + `${target.name && ' to '}${target.name}`,
        });
        // Log the damage received on that creature's log as well
        insertCreatureLog.call({
          log: {
            content: [{
              name,
              resultPrefix: 'Recieved ',
              result: damageDealt,
              details: suffix,
            }],
            creatureId: target._id,
          }
        });
      }
    });
  } else {
    // There are no targets, just log the result
    log.content.push({
      name: prop.damageType === 'healing' ? 'Healing' : 'Damage',
      result: result.toString(),
      details: suffix,
    });
  }
}
