import { Injectable } from '@angular/core';
import Joi from '@hapi/joi';
import {AbstractControl} from '@angular/forms';

@Injectable({
  providedIn: 'root'
})
export class ValidationService {

  private vUsername = Joi.string()
    .lowercase()
    .pattern(new RegExp('^[a-z0-9-_.]+$'))
    .min(1)
    .max(128);

  private vPassword = Joi.string()
    .min(8)
    .max(128);

  constructor() { }

  generate(method, args) {  // generate a Validator function
    return (control: AbstractControl): {[key: string]: any} | null => {
      let ok;
      let error;
      if (args) {
        ({ok, error} = this[method](control.value, ...args));
      }
      else {
        ({ok, error} = this[method](control.value));
      }
      return ok ? null : { failure: error };
    };
  }

  username(data) {
    const {ignore, error} = this.vUsername.validate(data);
    if (error) {
      return {ok: false, error: 'username must only contain a-z0-9-_.'};
    }
    return {ok: true, error: ''};
  }

  password(data) {
    const {ignore, error} = this.vPassword.validate(data);
    if (error) {
      return {ok: false, error: 'password must have at least 8 characters'};
    }
    return {ok: true, error: ''};
  }

  string(data, option = null) {
    let validator = Joi.string().max(128).allow('', true, false);
    let errorMsg = 'must contain max 128 characters';
    if (option === 'alphanum') {
      validator = validator.alphanum();
      errorMsg = 'must contain max 128 alphanumerical characters';
    }
    const {ignore, error} = validator.validate(data);
    if (error) {
      return {ok: false, error: errorMsg};
    }
    return {ok: true, error: ''};
  }

  stringOf(data, list) {  // string must be in list
    const {ignore, error} = Joi.string().allow('').valid(...list.map(e => e.toString())).validate(data);
    if (error) {
      return {ok: false, error: 'must be one of ' + list.join(', ')};
    }
    return {ok: true, error: ''};
  }

  stringNin(data, list) {  // string must not be in list
    const {ignore, error} = Joi.string().invalid(...list).validate(data);
    if (error) {
      return {ok: false, error: 'value not allowed'};
    }
    return {ok: true, error: ''};
  }

  stringLength(data, length) {  // string with maximum length
    const {ignore, error} = Joi.string().max(length).allow('').validate(data);
    if (error) {
      return {ok: false, error: 'must contain max ' + length + ' characters'};
    }
    return {ok: true, error: ''};
  }

  minMax(data, min, max) {  // number between min and max
    const {ignore, error} = Joi.number().allow('').min(min).max(max).validate(data);
    if (error) {
      return {ok: false, error: `must be between ${min} and ${max}`};
    }
    return {ok: true, error: ''};
  }

  min(data, min) {  // number above min
    const {ignore, error} = Joi.number().allow('').min(min).validate(data);
    if (error) {
      return {ok: false, error: `must not be below ${min}`};
    }
    return {ok: true, error: ''};
  }

  max(data, max) {  // number below max
    const {ignore, error} = Joi.number().allow('').max(max).validate(data);
    if (error) {
      return {ok: false, error: `must not be above ${max}`};
    }
    return {ok: true, error: ''};
  }

  url(data) {
    const {ignore, error} = Joi.string().uri().validate(data);
    if (error) {
      return {ok: false, error: `must be a valid URL`};
    }
    return {ok: true, error: ''};
  }

  unique(data, list) {
    const {ignore, error} = Joi.string().allow('').invalid(...list.map(e => e.toString())).validate(data);
    if (error) {
      return {ok: false, error: `values must be unique`};
    }
    return {ok: true, error: ''};
  }

  equal(data, compare) {
    if (data !== compare) {
      return {ok: false, error: `must be equal`};
    }
    return {ok: true, error: ''};
  }

  parameterName(data) {
    const {ignore, error} = Joi.string()
      .max(128)
      .invalid('condition_template', 'material_template')
      .allow('')
      .pattern(/^[^.]+$/)
      .required()
      .messages({'string.pattern.base': 'name must not contain a dot'})
      .validate(data);
    if (error) {
      return {ok: false, error: error.details[0].message};
    }
    return {ok: true, error: ''};
  }

  parameterRange(data) {
    if (data) {
      try {
        const {ignore, error} = Joi.object({
          values: Joi.array()
            .min(1),

          min: Joi.number(),

          max: Joi.number(),

          type: Joi.string()
            .valid('string', 'number', 'boolean', 'array'),

          required: Joi.boolean()
        })
          .oxor('values', 'min')
          .oxor('values', 'max')
          .required()
          .validate(JSON.parse(data));
        if (error) {
          return {ok: false, error: error.details[0].message};
        }
      }
      catch (e) {
        return {ok: false, error: `no valid JSON`};
      }
      return {ok: true, error: ''};
    }
    else {
      return {ok: false, error: `no valid value`};
    }
  }
}
