import { BOUND_LIMITS } from "../constants";
import { ChannelParams } from "../types";
import { toBoolean, toNumber } from "../utils";

/**
 * Validate the channel params object.
 *
 * @param channelParamsInput Channel params object to validate
 * @getters getResult(), getChannelParamsInput(), hasErrors(), getErrorsList()
 * @result Channel params object or null
 */
export default class ValidateChannelParams {
  private result;
  private errors: boolean = false;
  private errmsg: string[] = [];
  private channelParamsInput;

  constructor(channelParamsInput: ChannelParams[]) {
    /**
     * validate each of these for "hue", "saturation", "lightness"
     * 1. boundState is a boolean
     * 2. upperboundDivider is a number between BOUND_LIMITS.UPPERBOUND_DIVIDER.MIN and BOUND_LIMITS.UPPERBOUND_DIVIDER.MAX
     * 3. lowerboundDivider is a number between BOUND_LIMITS.LOWERBOUND_DIVIDER.MIN and BOUND_LIMITS.LOWERBOUND_DIVIDER.MAX
     * 4. upperboundPadding is a number between BOUND_LIMITS.UPPERBOUND_PADDING.MIN and BOUND_LIMITS.UPPERBOUND_PADDING.MAX
     * 5. lowerboundPadding is a number between BOUND_LIMITS.LOWERBOUND_PADDING.MIN and BOUND_LIMITS.LOWERBOUND_PADDING.MAX
     */

    this.channelParamsInput = channelParamsInput.map((channelParam) => {
      let { useBounds, upperboundDivider, lowerboundDivider, upperboundPadding, lowerboundPadding } = channelParam;

      useBounds = toBoolean(useBounds);
      upperboundDivider = toNumber(upperboundDivider);
      lowerboundDivider = toNumber(lowerboundDivider);
      upperboundPadding = toNumber(upperboundPadding);
      lowerboundPadding = toNumber(lowerboundPadding);

      if (!this.isBoundStateTypeValid(useBounds) || !useBounds) return;
      if (!this.isBoundValueTypeValid(upperboundDivider)) return;
      if (!this.isBoundValueTypeValid(lowerboundDivider)) return;
      if (!this.isBoundValueTypeValid(upperboundPadding)) return;
      if (!this.isBoundValueTypeValid(lowerboundPadding)) return;

      if (
        !this.isBoundWithinRange(
          upperboundDivider!,
          BOUND_LIMITS.MIN_UPPERBOUND_DIVIDER,
          BOUND_LIMITS.MAX_UPPERBOUND_DIVIDER
        )
      )
        return;

      if (
        !this.isBoundWithinRange(
          lowerboundDivider!,
          BOUND_LIMITS.MIN_LOWERBOUND_DIVIDER,
          BOUND_LIMITS.MAX_LOWERBOUND_DIVIDER
        )
      )
        return;

      if (
        !this.isBoundWithinRange(
          upperboundPadding!,
          BOUND_LIMITS.MIN_UPPERBOUND_PADDING,
          BOUND_LIMITS.MAX_UPPERBOUND_PADDING
        )
      )
        return;

      if (
        !this.isBoundWithinRange(
          lowerboundPadding!,
          BOUND_LIMITS.MIN_LOWERBOUND_PADDING,
          BOUND_LIMITS.MAX_LOWERBOUND_PADDING
        )
      )
        return;

      return {
        ...channelParam,
        useBounds,
        upperboundDivider,
        lowerboundDivider,
        upperboundPadding,
        lowerboundPadding,
      };
    });

    if (!this.errors) {
      this.result = this.channelParamsInput;
    }
  }

  get getResult() {
    return this.result;
  }

  get getChannelParamsInput() {
    return this.channelParamsInput;
  }

  get hasErrors() {
    return this.errors;
  }

  get getErrorsList() {
    return this.errmsg;
  }

  private addError(msg: string) {
    this.errmsg.push(msg);
  }

  private setErrorState() {
    this.errors = true;
  }

  private showErrors() {
    this.errmsg.forEach((msg) => {
      console.log(msg);
    });
  }

  private isBoundStateTypeValid(boundState: any) {
    if (typeof boundState !== "boolean") {
      this.addError("Bound state must be a boolean.");
      this.setErrorState();
      return false;
    }
    return true;
  }

  private isBoundValueTypeValid(boundValue: any) {
    if (typeof boundValue !== "number") {
      this.addError("Bound value must be a number.");
      this.setErrorState();
      return false;
    }
    return true;
  }

  private isBoundWithinRange(testValue: number, boundMin: number, boundMax: number) {
    if (testValue <= boundMin || testValue >= boundMax) {
      this.addError(`Bound value must be between ${boundMin} and ${boundMax}.`);
      this.setErrorState();
      return false;
    }
    return true;
  }
}
