import { AbstractControl, ValidationErrors } from '@angular/forms';
import { RangeFilter, RangeFilterOption } from '../../../models';
import { FilterGroup } from '../../filter-group';

export const maxValueRelativeTo = (relativeToSelector: (control: AbstractControl) => AbstractControl) => (control: AbstractControl) => {
  const relativeTo: AbstractControl = relativeToSelector?.call(null, control);

  if (!relativeTo || !relativeTo.value || !control.value) {
    return null;
  }

  if (+control.value > +relativeTo.value) {
    return { maxValueRelativeTo: `"Von" ${control.value} darf nicht größer sein als "Bis" ${relativeTo.value} sein.` };
  }

  return null;
};

export const minValueRelativeTo = (relativeToSelector: (control: AbstractControl) => AbstractControl) => (control: AbstractControl) => {
  const relativeTo: AbstractControl = relativeToSelector?.call(null, control);

  if (!relativeTo || !relativeTo.value || !control.value) {
    return null;
  }

  if (+control.value < +relativeTo.value) {
    return { maxValueRelativeTo: `"Bis" ${control.value} darf nicht kleiner sein als "Von" ${relativeTo.value} sein.` };
  }

  return null;
};

export function startValidator(filter: RangeFilter, filterGroup: FilterGroup, preSelectCategory: () => void) {
  return (control: AbstractControl): null | ValidationErrors => {
    const startOption: RangeFilterOption = filter.options[0];
    if (control.value !== startOption.value) {
      preSelectCategory();
      validateAndSetInput(control, startOption);
      const compare = compareWithStopValue(startOption.value, control);
      if (compare.error) {
        startOption.value = compare.value;
        filterGroup.setRangeOption(filter, startOption);
        return { valueRange: 'error' };
      } else {
        filterGroup.setRangeOption(filter, startOption);
      }
    }
    return null;
  };
}

export function stopValidator(filter: RangeFilter, filterGroup: FilterGroup, preSelectCategory: () => void) {
  return (control: AbstractControl): null | ValidationErrors => {
    const stopOption: RangeFilterOption = filter.options[1];
    if (control.value !== stopOption.value) {
      preSelectCategory();
      validateAndSetInput(control, stopOption);
      filterGroup.setRangeOption(filter, stopOption);

      const compare = compareWithStartValue(stopOption.value, control);
      if (compare.error) {
        control?.parent?.get('start')?.setValue('');
        return { valueRange: 'error' };
      }
    }
    return null;
  };
}

function validateAndSetInput(control: AbstractControl, option: RangeFilterOption) {
  control?.value?.length > 0 ? (option.selected = true) : (option.selected = false);
  option.value = checkValue(control, option);
}

function checkValue(control: AbstractControl, option: RangeFilterOption): string {
  let validatedResult = control.value;
  validatedResult = checkFormat(validatedResult);
  validatedResult = checkBoundaries(validatedResult, option);
  return validatedResult;
}

function checkFormat(value: string): string {
  return value
    .replace(/[^0-9.]/g, '') // Only Numbers
    .replace(/^(?:0+(?=[1-9])|0+(?=0$))/gm, '') // No leading Zeros and only one Zero allowed
    .replace(/(\..*)\./g, '$1'); // No special characters
}

function checkBoundaries(value: string, option: RangeFilterOption): string {
  if (Number(value) <= Number(option.maxValue) && Number(value) >= Number(option.minValue)) {
    return value;
  } else {
    return Number(value) > Number(option.maxValue) ? option.maxValue : Number(value) < Number(option.minValue) ? option.minValue : '';
  }
}

function compareWithStartValue(value: string, control: AbstractControl): { error: boolean } {
  const result = { error: false };
  const startCtrl = control?.parent?.get('start');
  if (Number(startCtrl.value) > Number(value) && startCtrl.value.length > 0 && value !== '') {
    result.error = true;
  }
  return result;
}

function compareWithStopValue(value: string, control: AbstractControl): { error: boolean; value: string } {
  const result = { error: false, value };
  const stopCtrl = control?.parent?.get('stop');
  if (Number(value) > Number(stopCtrl.value) && stopCtrl.value.length > 0) {
    result.value = stopCtrl.value;
    result.error = true;
  }
  return result;
}
