import { Injectable } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

import { IMetadataProperty, NewMetadataProperty } from '../metadata-property.model';

/**
 * A partial Type with required key is used as form input.
 */
type PartialWithRequiredKeyOf<T extends { id: unknown }> = Partial<Omit<T, 'id'>> & { id: T['id'] };

/**
 * Type for createFormGroup and resetForm argument.
 * It accepts IMetadataProperty for edit and NewMetadataPropertyFormGroupInput for create.
 */
type MetadataPropertyFormGroupInput = IMetadataProperty | PartialWithRequiredKeyOf<NewMetadataProperty>;

type MetadataPropertyFormDefaults = Pick<NewMetadataProperty, 'id' | 'mandatory' | 'organizationTypes'>;

type MetadataPropertyFormGroupContent = {
  id: FormControl<IMetadataProperty['id'] | NewMetadataProperty['id']>;
  code: FormControl<IMetadataProperty['code']>;
  name: FormControl<IMetadataProperty['name']>;
  description: FormControl<IMetadataProperty['description']>;
  mandatory: FormControl<IMetadataProperty['mandatory']>;
  metadataType: FormControl<IMetadataProperty['metadataType']>;
  pattern: FormControl<IMetadataProperty['pattern']>;
  version: FormControl<IMetadataProperty['version']>;
  organizationTypes: FormControl<IMetadataProperty['organizationTypes']>;
};

export type MetadataPropertyFormGroup = FormGroup<MetadataPropertyFormGroupContent>;

@Injectable({ providedIn: 'root' })
export class MetadataPropertyFormService {
  createMetadataPropertyFormGroup(metadataProperty: MetadataPropertyFormGroupInput = { id: null }): MetadataPropertyFormGroup {
    const metadataPropertyRawValue = {
      ...this.getFormDefaults(),
      ...metadataProperty,
    };
    return new FormGroup<MetadataPropertyFormGroupContent>({
      id: new FormControl(
        { value: metadataPropertyRawValue.id, disabled: true },
        {
          nonNullable: true,
          validators: [Validators.required],
        },
      ),
      code: new FormControl(metadataPropertyRawValue.code, {
        validators: [Validators.required, Validators.minLength(3)],
      }),
      name: new FormControl(metadataPropertyRawValue.name, {
        validators: [Validators.required],
      }),
      description: new FormControl(metadataPropertyRawValue.description),
      mandatory: new FormControl(metadataPropertyRawValue.mandatory),
      metadataType: new FormControl(metadataPropertyRawValue.metadataType, {
        validators: [Validators.required],
      }),
      pattern: new FormControl(metadataPropertyRawValue.pattern),
      version: new FormControl(metadataPropertyRawValue.version),
      organizationTypes: new FormControl(metadataPropertyRawValue.organizationTypes ?? []),
    });
  }

  getMetadataProperty(form: MetadataPropertyFormGroup): IMetadataProperty | NewMetadataProperty {
    return form.getRawValue() as IMetadataProperty | NewMetadataProperty;
  }

  resetForm(form: MetadataPropertyFormGroup, metadataProperty: MetadataPropertyFormGroupInput): void {
    const metadataPropertyRawValue = { ...this.getFormDefaults(), ...metadataProperty };
    form.reset(
      {
        ...metadataPropertyRawValue,
        id: { value: metadataPropertyRawValue.id, disabled: true },
      } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */,
    );
  }

  private getFormDefaults(): MetadataPropertyFormDefaults {
    return {
      id: null,
      mandatory: false,
      organizationTypes: [],
    };
  }
}
