/*
 * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
 * Ministerpräsidenten des Landes Schleswig-Holstein
 * Staatskanzlei
 * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
 *
 * Lizenziert unter der EUPL, Version 1.2 oder - sobald
 * diese von der Europäischen Kommission genehmigt wurden -
 * Folgeversionen der EUPL ("Lizenz");
 * Sie dürfen dieses Werk ausschließlich gemäß
 * dieser Lizenz nutzen.
 * Eine Kopie der Lizenz finden Sie hier:
 *
 * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
 *
 * Sofern nicht durch anwendbare Rechtsvorschriften
 * gefordert oder in schriftlicher Form vereinbart, wird
 * die unter der Lizenz verbreitete Software "so wie sie
 * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
 * ausdrücklich oder stillschweigend - verbreitet.
 * Die sprachspezifischen Genehmigungen und Beschränkungen
 * unter der Lizenz sind dem Lizenztext zu entnehmen.
 */
import {
  AbstractControl,
  FormControl,
  FormGroup,
  UntypedFormControl,
  UntypedFormGroup,
} from '@angular/forms';
import { faker } from '@faker-js/faker';
import { createInvalidParam, createIssue } from '../../../test/error';
import { InvalidParam, Issue } from '../tech.model';
import {
  getControlForInvalidParam,
  getControlForIssue,
  getFieldPath,
  getMessageForInvalidParam,
  getMessageForIssue,
  setInvalidParamValidationError,
  setIssueValidationError,
} from './tech.validation.util';

describe('ValidationUtils', () => {
  const baseField1Control: FormControl = new UntypedFormControl();
  const baseField2Control: FormControl = new UntypedFormControl();
  const subGroupFieldControl: FormControl = new UntypedFormControl();

  const form: FormGroup = new UntypedFormGroup({
    baseField1: baseField1Control,
    baseField2: baseField2Control,
    subGroup: new UntypedFormGroup({
      subGroupField1: subGroupFieldControl,
    }),
  });

  describe('set issue validation error', () => {
    describe('get control for issue', () => {
      it('should return base field control', () => {
        const issue: Issue = { ...createIssue(), field: 'class.resource.baseField1' };

        const control: AbstractControl = getControlForIssue(form, issue);

        expect(control).toBe(baseField1Control);
      });

      it('should return sub group field', () => {
        const issue: Issue = { ...createIssue(), field: 'class.resource.subGroup.subGroupField1' };

        const control: AbstractControl = getControlForIssue(form, issue, 'resource');

        expect(control).toBe(subGroupFieldControl);
      });

      it('should ignore path prefix', () => {
        const issue: Issue = { ...createIssue(), field: 'class.resource.baseField1' };

        const control: AbstractControl = getControlForIssue(form, issue, 'resource');

        expect(control).toBe(baseField1Control);
      });
    });

    describe('in base field', () => {
      const issue: Issue = { ...createIssue(), field: 'class.resource.baseField1' };

      it('should set error in control', () => {
        setIssueValidationError(form, issue);

        expect(baseField1Control.errors).not.toBeNull();
      });

      it('should set message code in control', () => {
        setIssueValidationError(form, issue);

        expect(baseField1Control.hasError(issue.messageCode)).toBe(true);
      });

      it('should set control touched', () => {
        setIssueValidationError(form, issue);

        expect(baseField1Control.touched).toBe(true);
      });

      it('should not set error in other control', () => {
        setIssueValidationError(form, issue);

        expect(baseField2Control.errors).toBeNull();
      });
    });

    describe('in subGroup Field', () => {
      const issue: Issue = { ...createIssue(), field: 'class.resource.subGroup.subGroupField1' };

      it('should set error in control', () => {
        setIssueValidationError(form, issue, 'resource');

        expect(subGroupFieldControl.errors).not.toBeNull();
      });
    });
  });

  describe('get message for issue', () => {
    const fieldLabel: string = 'Field Label';

    it('should return message', () => {
      const msg: string = getMessageForIssue(fieldLabel, {
        ...createIssue(),
        messageCode: 'validation_field_size',
      });

      expect(msg).toContain('muss mindestens');
    });

    it('should set field label', () => {
      const msg: string = getMessageForIssue(fieldLabel, {
        ...createIssue(),
        messageCode: 'validation_field_size',
      });

      expect(msg).toContain(fieldLabel);
    });

    it('should replace min param', () => {
      const msg: string = getMessageForIssue(fieldLabel, {
        ...createIssue(),
        messageCode: 'validation_field_size',
        parameters: [{ name: 'min', value: '3' }],
      });

      expect(msg).toContain('3');
    });
  });

  describe('invalid param', () => {
    const formPrefixes: string[] = ['', 'somePrefix'];
    const fieldNames: string[] = ['baseField1', 'baseField2', 'subGroup.subGroupField1'];
    const prefixNameCombinations: string[][] = formPrefixes
      .flatMap((prefix) => fieldNames.map((name) => [prefix, name]))
      .filter((el) => existingPrefixAndFieldCombination(el[0], el[1]));

    const fieldLabel: string = faker.lorem.word();

    describe.each(prefixNameCombinations)(
      'with prefix "%s" and fieldName "%s"',
      (prefix, fieldName) => {
        let invalidParam: InvalidParam;

        beforeEach(() => {
          form.reset();
          invalidParam = {
            ...createInvalidParam(),
            name: prefix.length ? `${prefix}.${fieldName}` : fieldName,
          };
        });

        describe('get message for invalid param', () => {
          it('should return', () => {
            const msg: string = getMessageForInvalidParam(fieldLabel, invalidParam);

            expect(msg).toEqual(`Bitte ${fieldLabel} ausfüllen`);
          });
        });

        describe('get control for invalid param', () => {
          it('should find', () => {
            const control: AbstractControl = getControlForInvalidParam(form, invalidParam, prefix);

            expect(control).toBeTruthy();
          });
        });

        describe('set invalid param validation error', () => {
          it('should assign invalidParam to form control error without prefix', () => {
            setInvalidParamValidationError(form, invalidParam, prefix);

            const result: InvalidParam = form.getError(invalidParam.reason, fieldName);

            expect(result).toBe(invalidParam);
          });

          it('should mark form as touched', () => {
            setInvalidParamValidationError(form, invalidParam, prefix);

            expect(form.touched).toBeTruthy();
          });
        });
      },
    );
  });

  describe('getFieldPath', () => {
    const resource: string = 'resource';
    const backendClassName: string = 'class';

    it('should return field path ', () => {
      const fieldPath: string = 'field1';
      const fullPath: string = `${backendClassName}.${resource}.${fieldPath}`;

      const result: string = getFieldPath(fullPath, resource);

      expect(result).toBe(fieldPath);
    });

    it('should get all parts after the prefix', () => {
      const fieldPath: string = 'group.field1';
      const fullPath: string = `${backendClassName}.${resource}.${fieldPath}`;

      const result: string = getFieldPath(fullPath, resource);

      expect(result).toBe(fieldPath);
    });
  });
});

function existingPrefixAndFieldCombination(prefix: string, field: string): boolean {
  return !(prefix === '' && field === 'subGroup.subGroupField1');
}