import { isInt } from '$lib/math';
import { canBeObjectId, int32, int64, uint64 } from '$lib/mongo';
import { jsonLooseParse } from '$lib/strings';

export default function input(node, { autofocus, type, onValid, onInvalid, mandatory } = {
  autofocus: false,
  type: '',
  onValid: () => 0,
  onInvalid: () => 0,
  mandatory: false,
}) {

  const getMessage = () => {
    const checkInteger = () => (isInt(node.value) ? false : 'Value must be an integer');
    const checkNumberBoundaries = boundaries => {
      if (node.value < boundaries[0]) {
        return `Input is too low for type ${type}`;
      }
      else if (node.value > boundaries[1]) {
        return `Input is too high for type ${type}`;
      }
      else {
        return true;
      }
    };

    switch (type) {
      case 'json':
        try {
          jsonLooseParse(node.value);
          return false;
        }
        catch {
          return 'Invalid JSON';
        }

      case 'int': // int32
        return checkInteger() || checkNumberBoundaries(int32);

      case 'long': // int64
        return checkInteger() || checkNumberBoundaries(int64);

      case 'uint64':
        return checkInteger() || checkNumberBoundaries(uint64);

      case 'string':
        if (mandatory && (!node.value)) {
          return 'This field cannot empty';
        }
        return false;

      case 'objectid':
        return !canBeObjectId(node.value) && 'Invalid string representation of an ObjectId';

      case 'double':
      case 'decimal':
      default:
        return false;
    }
  };

  const handleInput = () => {
    const invalid = getMessage();
    if (invalid) {
      node.classList.add('invalid');
      node.setCustomValidity(invalid);
      node.reportValidity();
      onInvalid?.();
    }
    else {
      node.classList.remove('invalid');
      node.setCustomValidity('');
      node.reportValidity();
      onValid?.();
    }
  };

  const handleFocus = () => {
    node.select();
  };

  node.addEventListener('focus', handleFocus);
  node.addEventListener('input', handleInput);

  if (autofocus) {
    node.focus();
  }

  return {
    destroy: () => {
      node.removeEventListener('focus', handleFocus);
      node.removeEventListener('input', handleInput);
    },
  };
}
