import { AbstractControl, ValidatorFn } from '@angular/forms';
import { ValidationError } from '@msfa-models/validation-error.model';

export function SocialSecurityNumberValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationError => {
    if (!control || !control.value) {
      return null;
    }

    const ssn = control.value as string;

    if (/[^0-9-]/g.test(ssn)) {
      return { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
    }

    let strippedSsn = ssn.replace(/[^0-9]/g, '');

    // Year format 1991 -> 91
    if (strippedSsn.length === 12) {
      strippedSsn = strippedSsn.substr(2);
    }

    // Check length
    if (strippedSsn.length !== 10) {
      return { type: 'ssnNotComplete', message: 'Personnummret är inte fullständigt' };
    }

    // Check month
    if (+strippedSsn.substr(2, 2) > 12 || strippedSsn.substr(2, 2) === '00') {
      return { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
    }

    // Check date (valid date + 60 is also apporved because of co-ordination number)
    if (
      (+strippedSsn.substr(4, 2) > 31 || strippedSsn.substr(4, 2) === '00') &&
      (+strippedSsn.substr(4, 2) > 91 || +strippedSsn.substr(4, 2) <= 60)
    ) {
      return { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
    }

    return isControlDigitLegit(strippedSsn) ? null : { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
  };
}

function isControlDigitLegit(ssn: string): boolean {
  // Run the mod 10 algorithm to produce check digit
  const enteredControlDigit = +ssn.substr(-1);
  let inc = 0;
  let multiplicator = 2;
  let product: number;

  for (const char of ssn.slice(0, -1)) {
    product = +char * multiplicator;
    if (product > 9) {
      inc += product - 9;
    } else {
      inc += product;
    }
    multiplicator = multiplicator === 1 ? 2 : 1;
  }

  let calculatedControlDigit = 10 - (inc - Math.floor(inc / 10) * 10);
  if (calculatedControlDigit === 10) {
    calculatedControlDigit = 0;
  }

  return calculatedControlDigit === enteredControlDigit;
}
