/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: MPL-2.0
 */

import Modifier from 'ember-modifier';
import { action } from '@ember/object';

class ValidationError extends Error {}

export default class ValidateModifier extends Modifier {
  item = null;
  hash = null;

  validate(value, validations = {}) {
    if (Object.keys(validations).length === 0) {
      return;
    }
    const errors = {};
    Object.entries(this.hash.validations)
      // filter out strings, for now these are helps, but ain't great if someone has a item.help
      .filter(([key, value]) => typeof value !== 'string')
      .forEach(([key, item]) => {
        // optionally set things for you
        if (this.item) {
          this.item[key] = value;
        }
        (item || []).forEach((validation) => {
          const re = new RegExp(validation.test);
          if (!re.test(value)) {
            errors[key] = new ValidationError(validation.error);
          }
        });
      });
    const state = this.hash.chart.state || {};
    if (state.context == null) {
      state.context = {};
    }
    if (Object.keys(errors).length > 0) {
      state.context.errors = errors;
      this.hash.chart.dispatch('ERROR', state.context);
    } else {
      state.context.errors = null;
      this.hash.chart.dispatch('RESET', state.context);
    }
  }

  @action
  reset(e) {
    if (e.target.value.length === 0) {
      const state = this.hash.chart.state;
      if (!state.context) {
        state.context = {};
      }
      if (!state.context.errors) {
        state.context.errors = {};
      }
      Object.entries(this.hash.validations)
        // filter out strings, for now these are helps, but ain't great if someone has a item.help
        .filter(([key, value]) => typeof value !== 'string')
        .forEach(([key, item]) => {
          if (typeof state.context.errors[key] !== 'undefined') {
            delete state.context.errors[key];
          }
        });
      if (Object.keys(state.context.errors).length === 0) {
        state.context.errors = null;
        this.hash.chart.dispatch('RESET', state.context);
      }
    }
  }

  async connect([value], _hash) {
    this.element.addEventListener('input', this.listen);
    this.element.addEventListener('blur', this.reset);
    if (this.element.value.length > 0) {
      await Promise.resolve();
      if (this && this.element) {
        this.validate(this.element.value, this.hash.validations);
      }
    }
  }

  @action
  listen(e) {
    this.validate(e.target.value, this.hash.validations);
  }

  disconnect() {
    this.item = null;
    this.hash = null;
    this.element.removeEventListener('input', this.listen);
    this.element.removeEventListener('blur', this.reset);
  }

  didReceiveArguments() {
    const [value] = this.args.positional;
    const _hash = this.args.named;

    this.item = value;
    this.hash = _hash;

    if (typeof _hash.chart === 'undefined') {
      this.hash.chart = {
        state: {
          context: {},
        },
        dispatch: (state) => {
          switch (state) {
            case 'ERROR':
              _hash.onchange(this.hash.chart.state.context.errors);
              break;
            case 'RESET':
              _hash.onchange();
              break;
          }
        },
      };
    }
  }

  didInstall() {
    this.connect(this.args.positional, this.args.named);
  }

  willRemove() {
    this.disconnect();
  }
}
