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

import dayjs from 'dayjs/esm';
import { DATE_TIME_FORMAT } from 'app/config/input.constants';
import { IInputDataUpload, NewInputDataUpload } from '../input-data-upload.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 IInputDataUpload for edit and NewInputDataUploadFormGroupInput for create.
 */
type InputDataUploadFormGroupInput = IInputDataUpload | PartialWithRequiredKeyOf<NewInputDataUpload>;

/**
 * Type that converts some properties for forms.
 */
type FormValueOf<T extends IInputDataUpload | NewInputDataUpload> = Omit<T, 'creationDate'> & {
  creationDate?: string | null;
};

type InputDataUploadFormRawValue = FormValueOf<IInputDataUpload>;

type NewInputDataUploadFormRawValue = FormValueOf<NewInputDataUpload>;

type InputDataUploadFormDefaults = Pick<NewInputDataUpload, 'id' | 'creationDate'>;

type InputDataUploadFormGroupContent = {
  id: FormControl<InputDataUploadFormRawValue['id'] | NewInputDataUpload['id']>;
  title: FormControl<InputDataUploadFormRawValue['title']>;
  dataFile: FormControl<InputDataUploadFormRawValue['dataFile']>;
  dataFileContentType: FormControl<InputDataUploadFormRawValue['dataFileContentType']>;
  uploadFileName: FormControl<InputDataUploadFormRawValue['uploadFileName']>;
  diskFileName: FormControl<InputDataUploadFormRawValue['diskFileName']>;
  type: FormControl<InputDataUploadFormRawValue['type']>;
  state: FormControl<InputDataUploadFormRawValue['state']>;
  comments: FormControl<InputDataUploadFormRawValue['comments']>;
  creationDate: FormControl<InputDataUploadFormRawValue['creationDate']>;
  creationUsername: FormControl<InputDataUploadFormRawValue['creationUsername']>;
  version: FormControl<InputDataUploadFormRawValue['version']>;
  period: FormControl<InputDataUploadFormRawValue['period']>;
  owner: FormControl<InputDataUploadFormRawValue['owner']>;
};

export type InputDataUploadFormGroup = FormGroup<InputDataUploadFormGroupContent>;

@Injectable({ providedIn: 'root' })
export class InputDataUploadFormService {
  createInputDataUploadFormGroup(inputDataUpload: InputDataUploadFormGroupInput = { id: null }): InputDataUploadFormGroup {
    const inputDataUploadRawValue = this.convertInputDataUploadToInputDataUploadRawValue({
      ...this.getFormDefaults(),
      ...inputDataUpload,
    });
    return new FormGroup<InputDataUploadFormGroupContent>({
      id: new FormControl(
        { value: inputDataUploadRawValue.id, disabled: true },
        {
          nonNullable: true,
          validators: [Validators.required],
        },
      ),
      title: new FormControl(inputDataUploadRawValue.title, {
        validators: [Validators.required],
      }),
      dataFile: new FormControl(inputDataUploadRawValue.dataFile, {
        validators: [Validators.required],
      }),
      dataFileContentType: new FormControl(inputDataUploadRawValue.dataFileContentType),
      uploadFileName: new FormControl(inputDataUploadRawValue.uploadFileName),
      diskFileName: new FormControl(inputDataUploadRawValue.diskFileName),
      type: new FormControl(inputDataUploadRawValue.type, {
        validators: [Validators.required],
      }),
      state: new FormControl(inputDataUploadRawValue.state),
      comments: new FormControl(inputDataUploadRawValue.comments),
      creationDate: new FormControl(inputDataUploadRawValue.creationDate),
      creationUsername: new FormControl(inputDataUploadRawValue.creationUsername),
      version: new FormControl(inputDataUploadRawValue.version),
      period: new FormControl(inputDataUploadRawValue.period, {
        validators: [Validators.required],
      }),
      owner: new FormControl(inputDataUploadRawValue.owner, {
        validators: [Validators.required],
      }),
    });
  }

  getInputDataUpload(form: InputDataUploadFormGroup): IInputDataUpload | NewInputDataUpload {
    return this.convertInputDataUploadRawValueToInputDataUpload(
      form.getRawValue() as InputDataUploadFormRawValue | NewInputDataUploadFormRawValue,
    );
  }

  resetForm(form: InputDataUploadFormGroup, inputDataUpload: InputDataUploadFormGroupInput): void {
    const inputDataUploadRawValue = this.convertInputDataUploadToInputDataUploadRawValue({ ...this.getFormDefaults(), ...inputDataUpload });
    form.reset(
      {
        ...inputDataUploadRawValue,
        id: { value: inputDataUploadRawValue.id, disabled: true },
      } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */,
    );
  }

  private getFormDefaults(): InputDataUploadFormDefaults {
    const currentTime = dayjs();

    return {
      id: null,
      creationDate: currentTime,
    };
  }

  private convertInputDataUploadRawValueToInputDataUpload(
    rawInputDataUpload: InputDataUploadFormRawValue | NewInputDataUploadFormRawValue,
  ): IInputDataUpload | NewInputDataUpload {
    return {
      ...rawInputDataUpload,
      creationDate: dayjs(rawInputDataUpload.creationDate, DATE_TIME_FORMAT),
    };
  }

  private convertInputDataUploadToInputDataUploadRawValue(
    inputDataUpload: IInputDataUpload | (Partial<NewInputDataUpload> & InputDataUploadFormDefaults),
  ): InputDataUploadFormRawValue | PartialWithRequiredKeyOf<NewInputDataUploadFormRawValue> {
    return {
      ...inputDataUpload,
      creationDate: inputDataUpload.creationDate ? inputDataUpload.creationDate.format(DATE_TIME_FORMAT) : undefined,
    };
  }
}
