import { Component, inject, OnInit } from '@angular/core';
import { HttpResponse } from '@angular/common/http';
import { ActivatedRoute } from '@angular/router';
import { Observable, tap, of } from 'rxjs';
import { finalize, map } from 'rxjs/operators';

import SharedModule from 'app/shared/shared.module';
import { FormsModule, ReactiveFormsModule, FormArray, Validators } from '@angular/forms';

import { IUnit } from 'app/entities/unit/unit.model';
import { UnitService } from 'app/entities/unit/service/unit.service';
import { IUnitConverter } from 'app/entities/unit-converter/unit-converter.model';
import { UnitConverterService } from 'app/entities/unit-converter/service/unit-converter.service';
import { IVariableScope } from 'app/entities/variable-scope/variable-scope.model';
import { VariableScopeService } from 'app/entities/variable-scope/service/variable-scope.service';
import { IVariableCategory } from 'app/entities/variable-category/variable-category.model';
import { VariableCategoryService } from 'app/entities/variable-category/service/variable-category.service';
import { IVariableClassType } from 'app/entities/variable-class-type/variable-class-type.model';
import { VariableClassTypeService } from 'app/entities/variable-class-type/service/variable-class-type.service';
import { InputMode } from 'app/entities/enumerations/input-mode.model';
import { VariableService } from '../service/variable.service';
import { IVariable } from '../variable.model';
import { VariableFormService, VariableFormGroup } from './variable-form.service';
import { VariableUnitsFormService, VariableUnitsFormGroup } from 'app/entities/variable-units/update/variable-units-form.service';
import { IVariableUnits } from 'app/entities/variable-units/variable-units.model';
import { IOrganization } from 'app/entities/organization/organization.model';
import { OrganizationService } from 'app/entities/organization/service/organization.service';
import { OrganizationNature } from 'app/entities/enumerations/organization-nature.model';
import { CodeMirrorFieldComponent } from 'app/resilient/codemirrorfield/codemirrorfield.component'

interface KeyValueArray {
  key: number;
  values: IUnitConverter[]; // or any other type for the inner array elements
}

@Component({
  standalone: true,
  selector: 'jhi-variable-update',
  templateUrl: './variable-update.component.html',
  styleUrl: './variable-update.component.css',
  imports: [SharedModule, FormsModule, ReactiveFormsModule, CodeMirrorFieldComponent],
})
export class VariableUpdateComponent implements OnInit {
  isSaving = false;
  variable: IVariable | null = null;
  inputModeValues = Object.keys(InputMode);
  currentTab: string = 'input'; // default tab
  isDisabled = true;
  isCollapsed = true;

  unitsSharedCollection: IUnit[] = [];
  unitsOfTypeSharedCollection: IUnit[] = [];
  variableScopesSharedCollection: IVariableScope[] = [];
  variableCategoriesSharedCollection: IVariableCategory[] = [];
  variableClassTypesSharedCollection: IVariableClassType[] = [];
  organizationsSharedCollection: IOrganization[] = [];

  /* unitConvertersSharedCollection: IUnitConverter[] = []; */
  unitConvertersSharedCollection = {};
  unitConvertersOptions: KeyValueArray[] = [];

  protected variableService = inject(VariableService);
  protected variableFormService = inject(VariableFormService);
  protected variableUnitsFormService = inject(VariableUnitsFormService);
  protected unitService = inject(UnitService);
  protected unitConverterService = inject(UnitConverterService);
  protected variableScopeService = inject(VariableScopeService);
  protected variableCategoryService = inject(VariableCategoryService);
  protected variableClassTypeService = inject(VariableClassTypeService);
  protected activatedRoute = inject(ActivatedRoute);
  protected organizationService = inject(OrganizationService);

  // eslint-disable-next-line @typescript-eslint/member-ordering
  editForm: VariableFormGroup = this.variableFormService.createVariableFormGroup();

  compareUnit = (o1: IUnit | null, o2: IUnit | null): boolean => this.unitService.compareEntity(o1, o2);
  compareUnitConverter = (o1: IUnitConverter | null, o2: IUnitConverter | null): boolean => this.unitConverterService.compareEntity(o1, o2);

  compareVariableScope = (o1: IVariableScope | null, o2: IVariableScope | null): boolean =>
    this.variableScopeService.compareEntity(o1, o2);

  compareVariableCategory = (o1: IVariableCategory | null, o2: IVariableCategory | null): boolean =>
    this.variableCategoryService.compareEntity(o1, o2);

  compareVariableClassType = (o1: IVariableClassType | null, o2: IVariableClassType | null): boolean =>
    this.variableClassTypeService.compareEntity(o1, o2);
	
  compareOrganization = (o1: IOrganization | null, o2: IOrganization | null): boolean =>
    this.organizationService.compareEntity(o1, o2);

  ngOnInit(): void {
    this.activatedRoute.data.subscribe(({ variable }) => {
      this.variable = variable;
      if (variable) {
        this.updateForm(variable);
      } else {
        this.protectBaseUnitSelect();
        this.variableClassTypeChange();
        this.protectInputTab();
        this.protectOutputTab();
        this.protectOutputSingle();
      }

      // Listen to changes to the scope filter property
      this.editForm.get('variableScope')?.valueChanges.subscribe((scope) => {
        this.editForm.get('variableCategory')?.setValue(null);
        this.loadCategoriesOfSelectedScope(scope).subscribe((cats: IVariableCategory[] | null) => {
          if (cats) {
            this.variableCategoriesSharedCollection = cats;
          }
        });
      });
      
      this.loadRelationshipsOptions();
    });
	
    this.editForm.get('variableUnits')?.valueChanges.subscribe(changes=> {
      this.protectBaseUnitSelect();
    })
	
    this.editForm.get('variableClassType')?.valueChanges.subscribe((selectedItem) => {
      this.variableClassTypeChange();
    });
	
	  this.editForm.get('input')?.valueChanges.subscribe((value) => {
      this.protectInputTab();
    });
	
	  this.editForm.get('output')?.valueChanges.subscribe((value) => {
      this.protectOutputTab();
    });

	  this.editForm.get('outputSingle')?.valueChanges.subscribe((value) => {
      this.protectOutputSingle();
    });
  }

  previousState(): void {
    window.history.back();
  }

  save(): void {
    this.isSaving = true;
    const variable = this.variableFormService.getVariable(this.editForm);
    if (variable.id !== null) {
      this.subscribeToSaveResponse(this.variableService.update(variable));
    } else {
      this.subscribeToSaveResponse(this.variableService.create(variable));
    }
  }

  setTab(tabId: string): void {
    this.currentTab = tabId;
  }
  
  /* Nested collection methods : VariableUnits's */
  get variableUnits(): FormArray {
	return this.editForm.get('variableUnits') as FormArray;
  }
  
  addVariableUnit(varUnit: IVariableUnits | null): void {
	// Create new instance of the VariableUnit form group
	let variableUnitsFormGroup: VariableUnitsFormGroup;
	if (varUnit) {
		variableUnitsFormGroup = this.variableUnitsFormService.createVariableUnitsFormGroup(varUnit);
		this.loadVariableConvertersForUnit(variableUnitsFormGroup, varUnit.unit, false);
	} else {
		variableUnitsFormGroup = this.variableUnitsFormService.createVariableUnitsFormGroup();
	}
	
	// Listen to changes to the VariableUnit.unit property
	variableUnitsFormGroup.get('unit')?.valueChanges.subscribe((unit) => {
      this.loadVariableConvertersForUnit(variableUnitsFormGroup, unit, true);
	  
      // Set validator
      const baseUnit = this.editForm.get('baseUnit')?.value;
      const unitConverterControl = variableUnitsFormGroup.get('unitConverter');
      if (baseUnit && unit && unitConverterControl) {
        if (baseUnit.unitType?.id === unit.unitType?.id) {
          unitConverterControl.clearValidators();
        } else {
          unitConverterControl.setValidators([Validators.required]); 
        }
        
        // Update the validity of the control
        unitConverterControl.updateValueAndValidity();
      }
    });
	
	// Add it to the FormArray
    this.variableUnits.push( variableUnitsFormGroup );
  }

  removeVariableUnit(index: number): void {
    this.variableUnits.removeAt(index);
  }
  
  // Load subcategory options based on the selected category
  loadVariableConvertersForUnit(variableUnitsFormGroup: VariableUnitsFormGroup, unit: Pick<IUnit, 'id' | 'name' | 'unitType'> | null | undefined, clearUnitConverter: boolean): void {
    const baseUnit = this.editForm.get('baseUnit')?.value;
    const unitConverterControl = variableUnitsFormGroup.get('unitConverter');
    
    if (unitConverterControl) {
     if (clearUnitConverter) { unitConverterControl.setValue(null); }
     
     if (baseUnit && unit) {
       // Enable control for editing
       unitConverterControl.enable();
       
       // Call server for UnitConverter loading
         this.unitConverterService
           .queryByFromAndTo(unit.id, baseUnit.id)
           .pipe(map((res: HttpResponse<IUnitConverter[]>) => res.body ?? []))
           .subscribe((unitConverters: IUnitConverter[]) => {
             variableUnitsFormGroup.get('unitConverterOptions')?.setValue( unitConverters ); 	// Save options in FormGroup
           });
     } else {
       unitConverterControl.disable();
     }
    }
  }

  protected variableClassTypeChange(): void {
	  const selectedValue = this.editForm.get('variableClassType')?.value;
	  
	  if (selectedValue) {
      this.editForm.get('variableClassLabel')?.enable();
      this.editForm.get('variableClassMandatory')?.enable();
	  } else {
      this.editForm.get('variableClassLabel')?.setValue(null);
      this.editForm.get('variableClassLabel')?.disable();
      this.editForm.get('variableClassMandatory')?.setValue(null);
      this.editForm.get('variableClassMandatory')?.disable();  
	  }
  }

  protected protectInputTab(): void {
	 const input = this.editForm.get("input")?.value;
	 if (input) {
		 this.editForm.get('inputMode')?.enable();
		 this.editForm.get('inputMode')?.setValidators([Validators.required]); 
     this.editForm.get('hiddenForMain')?.enable();
	 } else {
		 this.editForm.get('inputMode')?.setValue(null);
		 this.editForm.get('inputMode')?.disable();
		 this.editForm.get('inputMode')?.clearValidators();
     this.editForm.get('hiddenForMain')?.disable();
	 }
	 this.editForm.get('inputMode')?.updateValueAndValidity();
  }
  
  protected protectOutputTab(): void {
	 const output = this.editForm.get("output")?.value;
	 if (output) {
     this.editForm.get('outputSingle')?.enable();
		 this.editForm.get('outputFormula')?.enable();
		 this.editForm.get('outputFormula')?.setValidators([Validators.required]);
	 } else {
		 this.editForm.get('outputFormula')?.setValue(null);
		 this.editForm.get('outputFormula')?.disable();
     this.editForm.get('outputSingle')?.setValue(false);
     this.editForm.get('outputSingle')?.disable();
		 this.editForm.get('outputFormula')?.clearValidators();
	 }
	 this.editForm.get('outputFormula')?.updateValueAndValidity();
  }
  
  protected protectOutputSingle(): void {
    const outputSingle = this.editForm.get("outputSingle")?.value;
    if (outputSingle) {
      this.editForm.get('outputOwner')?.enable();
      this.editForm.get('outputOwner')?.setValidators([Validators.required]);
    } else {
      this.editForm.get('outputOwner')?.setValue(null);
      this.editForm.get('outputOwner')?.disable();
      this.editForm.get('outputOwner')?.clearValidators();
    }
    this.editForm.get('outputOwner')?.updateValueAndValidity();
  }

  protected protectBaseUnitSelect(): void {
	 const formArrayLength = this.variableUnits.length;
	 if (formArrayLength > 0) {
		 this.editForm.get('baseUnit')?.disable();
	 } else {
		 this.editForm.get('baseUnit')?.enable();
	 }
  }
  
  protected subscribeToSaveResponse(result: Observable<HttpResponse<IVariable>>): void {
    result.pipe(finalize(() => this.onSaveFinalize())).subscribe({
      next: () => this.onSaveSuccess(),
      error: () => this.onSaveError(),
    });
  }

  protected onSaveSuccess(): void {
    this.previousState();
  }

  protected onSaveError(): void {
    // Api for inheritance.
  }

  protected onSaveFinalize(): void {
    this.isSaving = false;
  }

  protected updateForm(variable: IVariable): void {
    this.variable = variable;
    this.variableFormService.resetForm(this.editForm, variable);

    // variable.variableUnits FormArray
    variable.variableUnits?.forEach(varUnit => {
      this.addVariableUnit(varUnit);
    });
	
    this.unitsSharedCollection = this.unitService.addEntityToCollectionIfMissing<IUnit>(this.unitsSharedCollection, variable.baseUnit);
	
    this.variableScopesSharedCollection = this.variableScopeService.addEntityToCollectionIfMissing<IVariableScope>(
      this.variableScopesSharedCollection,
      variable.variableScope,
    );
    
    this.organizationsSharedCollection = this.organizationService.addEntityToCollectionIfMissing<IOrganization>(
      this.organizationsSharedCollection,
      variable.outputOwner,
    );

    this.loadCategoriesOfSelectedScope(variable.variableScope).subscribe((cats: IVariableCategory[] | null) => {
      if (cats) {
        this.variableCategoriesSharedCollection = cats;
      }
      
      this.variableCategoriesSharedCollection = this.variableCategoryService.addEntityToCollectionIfMissing<IVariableCategory>(
        this.variableCategoriesSharedCollection,
        variable.variableCategory,
      );
    });
	
    this.protectBaseUnitSelect();
    this.variableClassTypeChange();
    this.protectInputTab();
    this.protectOutputTab();
    this.protectOutputSingle();
  }
  
  protected loadUnitsOfTypeSharedCollection(baseUnitId: number|null): void {
    if (baseUnitId) {
      this.unitService
          .queryByType(baseUnitId)
          .pipe(map((res: HttpResponse<IUnit[]>) => res.body ?? []))
          .subscribe((units: IUnit[]) => (this.unitsOfTypeSharedCollection = units));
    } else {
      this.unitsOfTypeSharedCollection = [];
    }
  }
  
  protected loadCategoriesOfSelectedScope(scope: IVariableScope|null|undefined): Observable<IVariableCategory[] | null> {
    if (scope) {
      /*
      this.variableCategoryService
          .queryByScopeForData(scope.id)
          .pipe(map((res: HttpResponse<IVariableCategory[]>) => res.body ?? []))
          .subscribe((cats: IVariableCategory[]) => (this.variableCategoriesSharedCollection = cats));
      */
      return this.variableCategoryService
              .queryByScope(scope.id)
              .pipe(map((res: HttpResponse<IVariableCategory[]>) => res.body ?? []));
    } else {
      // this.variableCategoriesSharedCollection = [];
      return of(null).pipe(
        tap(() => {
          this.variableCategoriesSharedCollection = [];
        })
      );
    }
  }
  
  protected loadRelationshipsOptions(): void {
    this.unitService
      .query()
      .pipe(map((res: HttpResponse<IUnit[]>) => res.body ?? []))
      .pipe(map((units: IUnit[]) => this.unitService.addEntityToCollectionIfMissing<IUnit>(units, this.variable?.baseUnit)))
      .subscribe((units: IUnit[]) => (this.unitsSharedCollection = units));

    this.unitConverterService
      .query()
      .pipe(map((res: HttpResponse<IUnitConverter[]>) => res.body ?? []))
      /* .pipe(map((units: IUnit[]) => this.unitService.addEntityToCollectionIfMissing<IUnit>(units, this.variable?.baseUnit))) */
      .subscribe((unitConverters: IUnitConverter[]) => (this.unitConvertersSharedCollection = unitConverters));
	
    this.variableScopeService
      .query()
      .pipe(map((res: HttpResponse<IVariableScope[]>) => res.body ?? []))
      .pipe(
        map((variableScopes: IVariableScope[]) =>
          this.variableScopeService.addEntityToCollectionIfMissing<IVariableScope>(variableScopes, this.variable?.variableScope),
        ),
      )
      .subscribe((variableScopes: IVariableScope[]) => (this.variableScopesSharedCollection = variableScopes));
    
    this.variableClassTypeService
      .query()
      .pipe(map((res: HttpResponse<IVariableClassType[]>) => res.body ?? []))
      .pipe(
        map((variableClassTypes: IVariableClassType[]) =>
          this.variableClassTypeService.addEntityToCollectionIfMissing<IVariableClassType>(
            variableClassTypes,
            this.variable?.variableClassType,
          ),
        ),
      )
      .subscribe((variableClassTypes: IVariableClassType[]) => (this.variableClassTypesSharedCollection = variableClassTypes));

    this.organizationService
      .queryByNature(OrganizationNature.ORGANIZATION)
      .pipe(map((res: HttpResponse<IOrganization[]>) => res.body ?? []))
      .pipe(
        map((organizations: IOrganization[]) =>
          this.organizationService.addEntityToCollectionIfMissing<IOrganization>(organizations, this.variable?.outputOwner),
        ),
      )
      .subscribe((organizations: IOrganization[]) => {
	    this.organizationsSharedCollection = organizations;
	  });
  }
}

