import { dispel } from '../../dispel/dispel';
import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint';
import { AssertionFailed, InvalidOperation } from '../../errors';
import { error, isResult, Result, value } from 'defekt';
import { isArray, isBoolean, isError, isFunction, isMap, isNull, isNumber, isObject, isSet, isString, isSymbol, isUndefined, Type } from 'typedescriptor';

const assertAnyIsNotOfType = function (
  actual: any,
  expected: Type | 'result'
): Result<undefined, AssertionFailed> {
  const dispelledActual = dispel(actual);

  switch (expected) {
    case 'array': {
      if (isArray(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type array.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'boolean': {
      if (isBoolean(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type boolean.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'error': {
      if (isError(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type error.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'function': {
      if (isFunction(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type function.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'map': {
      if (isMap(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type map.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'null': {
      if (isNull(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type null.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'number': {
      if (isNumber(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type number.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'result': {
      if (isResult(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type result.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'set': {
      if (isSet(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type set.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'string': {
      if (isString(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type string.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'symbol': {
      if (isSymbol(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type symbol.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'undefined': {
      if (isUndefined(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type undefined.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    case 'object': {
      if (isObject(actual)) {
        return error(new AssertionFailed({
          message: 'The value is of type object.',
          actual: prettyPrint(dispelledActual)
        }));
      }
      break;
    }
    default: {
      throw new InvalidOperation();
    }
  }

  return value();
};

export {
  assertAnyIsNotOfType
};
