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

import dayjs from 'dayjs/esm';
import { DATE_TIME_FORMAT } from 'app/config/input.constants';
import { IPeriod, NewPeriod } from '../period.model';

import { dateRangeValidator, dateLTValidator, dateGTValidator  } from '../../resilient/resilient-validators-form.service'

/**
 * 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 IPeriod for edit and NewPeriodFormGroupInput for create.
 */
type PeriodFormGroupInput = IPeriod | PartialWithRequiredKeyOf<NewPeriod>;

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

type PeriodFormRawValue = FormValueOf<IPeriod>;

type NewPeriodFormRawValue = FormValueOf<NewPeriod>;

type PeriodFormDefaults = Pick<NewPeriod, 'id' | 'creationDate'>;

type PeriodFormGroupContent = {
  id: FormControl<PeriodFormRawValue['id'] | NewPeriod['id']>;
  name: FormControl<PeriodFormRawValue['name']>;
  description: FormControl<PeriodFormRawValue['description']>;
  beginDate: FormControl<PeriodFormRawValue['beginDate']>;
  endDate: FormControl<PeriodFormRawValue['endDate']>;
  state: FormControl<PeriodFormRawValue['state']>;
  creationDate: FormControl<PeriodFormRawValue['creationDate']>;
  creationUsername: FormControl<PeriodFormRawValue['creationUsername']>;
  version: FormControl<PeriodFormRawValue['version']>;
};

export type PeriodFormGroup = FormGroup<PeriodFormGroupContent>;

@Injectable({ providedIn: 'root' })
export class PeriodFormService {
  constructor(private fb: FormBuilder) {} 
  
  createPeriodFormGroup(period: PeriodFormGroupInput = { id: null }): PeriodFormGroup {
    const periodRawValue = this.convertPeriodToPeriodRawValue({
      ...this.getFormDefaults(),
      ...period,
    });
	
    const items = period?.periodVersions??[];
	
    return new FormGroup<PeriodFormGroupContent>({
      id: new FormControl(
        { value: periodRawValue.id, disabled: true },
        {
          nonNullable: true,
          validators: [Validators.required],
        },
      ),
      name: new FormControl(periodRawValue.name, {
        validators: [Validators.required],
      }),
      description: new FormControl(periodRawValue.description),
      beginDate: new FormControl(periodRawValue.beginDate, {
        validators: [Validators.required, dateLTValidator('endDate')],
      }),
      endDate: new FormControl(periodRawValue.endDate, {
        validators: [Validators.required, dateGTValidator('beginDate')],
      }),
      state: new FormControl(periodRawValue.state),
      creationDate: new FormControl(periodRawValue.creationDate),
      creationUsername: new FormControl(periodRawValue.creationUsername),
      version: new FormControl(periodRawValue.version)
    });
  }

  getPeriod(form: PeriodFormGroup): IPeriod | NewPeriod {
    return this.convertPeriodRawValueToPeriod(form.getRawValue() as PeriodFormRawValue | NewPeriodFormRawValue);
  }

  resetForm(form: PeriodFormGroup, period: PeriodFormGroupInput): void {
    const periodRawValue = this.convertPeriodToPeriodRawValue({ ...this.getFormDefaults(), ...period });
    form.reset(
      {
        ...periodRawValue,
        id: { value: periodRawValue.id, disabled: true },
      } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */,
    );
  }

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

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

  private convertPeriodRawValueToPeriod(rawPeriod: PeriodFormRawValue | NewPeriodFormRawValue): IPeriod | NewPeriod {
    return {
      ...rawPeriod,
      creationDate: dayjs(rawPeriod.creationDate, DATE_TIME_FORMAT),
    };
  }

  private convertPeriodToPeriodRawValue(
    period: IPeriod | (Partial<NewPeriod> & PeriodFormDefaults),
  ): PeriodFormRawValue | PartialWithRequiredKeyOf<NewPeriodFormRawValue> {
    return {
      ...period,
      creationDate: period.creationDate ? period.creationDate.format(DATE_TIME_FORMAT) : undefined,
    };
  }
}
