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 { IInputDataUploadLog, NewInputDataUploadLog } from '../input-data-upload-log.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 IInputDataUploadLog for edit and NewInputDataUploadLogFormGroupInput for create.
 */
type InputDataUploadLogFormGroupInput = IInputDataUploadLog | PartialWithRequiredKeyOf<NewInputDataUploadLog>;

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

type InputDataUploadLogFormRawValue = FormValueOf<IInputDataUploadLog>;

type NewInputDataUploadLogFormRawValue = FormValueOf<NewInputDataUploadLog>;

type InputDataUploadLogFormDefaults = Pick<NewInputDataUploadLog, 'id' | 'creationDate'>;

type InputDataUploadLogFormGroupContent = {
  id: FormControl<InputDataUploadLogFormRawValue['id'] | NewInputDataUploadLog['id']>;
  logMessage: FormControl<InputDataUploadLogFormRawValue['logMessage']>;
  creationDate: FormControl<InputDataUploadLogFormRawValue['creationDate']>;
  creationUsername: FormControl<InputDataUploadLogFormRawValue['creationUsername']>;
  version: FormControl<InputDataUploadLogFormRawValue['version']>;
  inputDataUpload: FormControl<InputDataUploadLogFormRawValue['inputDataUpload']>;
};

export type InputDataUploadLogFormGroup = FormGroup<InputDataUploadLogFormGroupContent>;

@Injectable({ providedIn: 'root' })
export class InputDataUploadLogFormService {
  createInputDataUploadLogFormGroup(inputDataUploadLog: InputDataUploadLogFormGroupInput = { id: null }): InputDataUploadLogFormGroup {
    const inputDataUploadLogRawValue = this.convertInputDataUploadLogToInputDataUploadLogRawValue({
      ...this.getFormDefaults(),
      ...inputDataUploadLog,
    });
    return new FormGroup<InputDataUploadLogFormGroupContent>({
      id: new FormControl(
        { value: inputDataUploadLogRawValue.id, disabled: true },
        {
          nonNullable: true,
          validators: [Validators.required],
        },
      ),
      logMessage: new FormControl(inputDataUploadLogRawValue.logMessage, {
        validators: [Validators.required],
      }),
      creationDate: new FormControl(inputDataUploadLogRawValue.creationDate, {
        validators: [Validators.required],
      }),
      creationUsername: new FormControl(inputDataUploadLogRawValue.creationUsername, {
        validators: [Validators.required],
      }),
      version: new FormControl(inputDataUploadLogRawValue.version),
      inputDataUpload: new FormControl(inputDataUploadLogRawValue.inputDataUpload),
    });
  }

  getInputDataUploadLog(form: InputDataUploadLogFormGroup): IInputDataUploadLog | NewInputDataUploadLog {
    return this.convertInputDataUploadLogRawValueToInputDataUploadLog(
      form.getRawValue() as InputDataUploadLogFormRawValue | NewInputDataUploadLogFormRawValue,
    );
  }

  resetForm(form: InputDataUploadLogFormGroup, inputDataUploadLog: InputDataUploadLogFormGroupInput): void {
    const inputDataUploadLogRawValue = this.convertInputDataUploadLogToInputDataUploadLogRawValue({
      ...this.getFormDefaults(),
      ...inputDataUploadLog,
    });
    form.reset(
      {
        ...inputDataUploadLogRawValue,
        id: { value: inputDataUploadLogRawValue.id, disabled: true },
      } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */,
    );
  }

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

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

  private convertInputDataUploadLogRawValueToInputDataUploadLog(
    rawInputDataUploadLog: InputDataUploadLogFormRawValue | NewInputDataUploadLogFormRawValue,
  ): IInputDataUploadLog | NewInputDataUploadLog {
    return {
      ...rawInputDataUploadLog,
      creationDate: dayjs(rawInputDataUploadLog.creationDate, DATE_TIME_FORMAT),
    };
  }

  private convertInputDataUploadLogToInputDataUploadLogRawValue(
    inputDataUploadLog: IInputDataUploadLog | (Partial<NewInputDataUploadLog> & InputDataUploadLogFormDefaults),
  ): InputDataUploadLogFormRawValue | PartialWithRequiredKeyOf<NewInputDataUploadLogFormRawValue> {
    return {
      ...inputDataUploadLog,
      creationDate: inputDataUploadLog.creationDate ? inputDataUploadLog.creationDate.format(DATE_TIME_FORMAT) : undefined,
    };
  }
}
