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

import { IVariable, NewVariable } from '../variable.model';
import { VariableUnitsFormGroup, VariableUnitsFormService } from '../../variable-units/update/variable-units-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 IVariable for edit and NewVariableFormGroupInput for create.
 */
type VariableFormGroupInput = IVariable | PartialWithRequiredKeyOf<NewVariable>;

type VariableFormDefaults = Pick<NewVariable, 'id' | 'input' | 'output' | 'variableClassMandatory'>;

type VariableFormGroupContent = {
  id: FormControl<IVariable['id'] | NewVariable['id']>;
  code: FormControl<IVariable['code']>;
  variableIndex: FormControl<IVariable['variableIndex']>;
  name: FormControl<IVariable['name']>;
  description: FormControl<IVariable['description']>;
  input: FormControl<IVariable['input']>;
  inputMode: FormControl<IVariable['inputMode']>;
  output: FormControl<IVariable['output']>;
  outputSingle: FormControl<IVariable['outputSingle']>;
  outputOwner: FormControl<IVariable['outputOwner']>;
  outputFormula: FormControl<IVariable['outputFormula']>;
  valueScale: FormControl<IVariable['valueScale']>;
  version: FormControl<IVariable['version']>;
  baseUnit: FormControl<IVariable['baseUnit']>;
  variableScope: FormControl<IVariable['variableScope']>;
  variableCategory: FormControl<IVariable['variableCategory']>;
  variableClassType: FormControl<IVariable['variableClassType']>;
  variableClassMandatory?: FormControl<IVariable['variableClassMandatory']>;
  hiddenForMain?: FormControl<IVariable['hiddenForMain']>;
  variableClassLabel?: FormControl<IVariable['variableClassLabel']>;
  variableUnits: FormArray<VariableUnitsFormGroup>;
};

export type VariableFormGroup = FormGroup<VariableFormGroupContent>;

@Injectable({ providedIn: 'root' })
export class VariableFormService {
  constructor(private fb: FormBuilder,  
			  private variableUnitsFormService: VariableUnitsFormService) {} 
  
  createVariableFormGroup(variable: VariableFormGroupInput = { id: null }): VariableFormGroup {
    const variableRawValue = {
      ...this.getFormDefaults(),
      ...variable,
    };
	
	const items = variable?.variableUnits??[];
	
    return new FormGroup<VariableFormGroupContent>({
      id: new FormControl(
        { value: variableRawValue.id, disabled: true },
        {
          nonNullable: true,
          validators: [Validators.required],
        },
      ),
      code: new FormControl(variableRawValue.code, {
        validators: [Validators.required, Validators.pattern('^[0-9]*$')],
      }),
      variableIndex: new FormControl(variableRawValue.variableIndex),
      name: new FormControl(variableRawValue.name, {
        validators: [Validators.required],
      }),
      description: new FormControl(variableRawValue.description, {
        validators: [Validators.required],
      }),
      input: new FormControl(variableRawValue.input, {
        validators: [Validators.required],
      }),
      inputMode: new FormControl(variableRawValue.inputMode),
      output: new FormControl(variableRawValue.output, {
        validators: [Validators.required],
      }),
      outputSingle: new FormControl(variableRawValue.outputSingle),
      outputOwner: new FormControl(variableRawValue.outputOwner),
      outputFormula: new FormControl(variableRawValue.outputFormula),
      valueScale: new FormControl(variableRawValue.valueScale),
      version: new FormControl(variableRawValue.version),
      baseUnit: new FormControl(variableRawValue.baseUnit, {
        validators: [Validators.required],
      }),
      variableScope: new FormControl(variableRawValue.variableScope),
      variableCategory: new FormControl(variableRawValue.variableCategory),
      variableClassType: new FormControl(variableRawValue.variableClassType),
      variableClassMandatory: new FormControl(variableRawValue.variableClassMandatory),
      hiddenForMain: new FormControl(variableRawValue.hiddenForMain),
      variableClassLabel: new FormControl(variableRawValue.variableClassLabel),
      variableUnits: new FormArray(items.map(item => this.variableUnitsFormService.createVariableUnitsFormGroup(item)))
    });
  }

  getVariable(form: VariableFormGroup): IVariable | NewVariable {
    return form.getRawValue() as IVariable | NewVariable;
  }

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

  private getFormDefaults(): VariableFormDefaults {
    return {
      id: null,
      input: false,
      output: false,
	  variableClassMandatory: false,
    };
  }
}
