import { Component, NgZone, inject, OnInit } from '@angular/core';
import { ActivatedRoute, Data, ParamMap, Router, RouterModule } from '@angular/router';
import { combineLatest, filter, Observable, Subscription, tap, map } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { HttpResponse } from '@angular/common/http';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';

import SharedModule from 'app/shared/shared.module';
import { sortStateSignal, SortDirective, SortByDirective, type SortState, SortService } from 'app/shared/sort';
import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants';

import { IPeriod } from 'app/entities/period/period.model';
import { IPeriodVersion } from 'app/entities/period-version/period-version.model';
import { IOrganization } from 'app/entities/organization/organization.model';
import { IVariableScope } from 'app/entities/variable-scope/variable-scope.model';
import { IVariableCategory } from 'app/entities/variable-category/variable-category.model';
import { PeriodService } from 'app/entities/period/service/period.service';
import { PeriodVersionService } from 'app/entities/period-version/service/period-version.service';
import { OrganizationService } from 'app/entities/organization/service/organization.service';
import { VariableScopeService } from 'app/entities/variable-scope/service/variable-scope.service';
import { VariableCategoryService } from 'app/entities/variable-category/service/variable-category.service';
import { IOutputData } from '../output-data.model';
import { EntityArrayResponseType, OutputDataService } from '../service/output-data.service';
import { OutputDataSearchFormService, OutputDataSearchFormGroup } from './output-data-search-form.service';
import { ResilientEnvironmentService } from 'app/resilient/resilient-environment/service/resilient-environment.service';

@Component({
  standalone: true,
  selector: 'jhi-output-data',
  templateUrl: './output-data.component.html',
  styleUrl: './output-data.component.css',
  imports: [
    RouterModule,
    FormsModule,
    ReactiveFormsModule,
    SharedModule,
    SortDirective,
    SortByDirective,
    DurationPipe,
    FormatMediumDatetimePipe,
    FormatMediumDatePipe,
    MatProgressSpinnerModule,
  ],
})
export class OutputDataComponent implements OnInit {
  subscriptionWorkOrganization!: Subscription;  // Store the subscription
  selectedOrganization?: IOrganization;
  subscription: Subscription | null = null;
  outputData?: IOutputData[];
  
  isLoading = false;
  isExporting: boolean = false;

  sortState = sortStateSignal({});

  scopesSharedCollection: IVariableScope[] = [];
  categoriesSharedCollection: IVariableCategory[] = [];
  periodsSharedCollection: IPeriod[] = [];
  periodVersionsSharedCollection: IPeriodVersion[] = [];
  orgsSharedCollection: IOrganization[] = [];
  
  public router = inject(Router);
  protected variableScopeService = inject(VariableScopeService);
  protected variableCategoryService = inject(VariableCategoryService);
  protected periodService = inject(PeriodService);
  protected periodVersionService = inject(PeriodVersionService);
  protected organizationService = inject(OrganizationService);
  protected outputDataService = inject(OutputDataService);
  protected outputDataSearchFormService = inject(OutputDataSearchFormService);
  protected activatedRoute = inject(ActivatedRoute);
  protected sortService = inject(SortService);
  protected modalService = inject(NgbModal);
  protected ngZone = inject(NgZone);
  protected envService = inject(ResilientEnvironmentService);

  searchForm: OutputDataSearchFormGroup = this.outputDataSearchFormService.createOutputDataSearchFormGroup();
  trackId = (_index: number, item: IOutputData): number => this.outputDataService.getEntityIdentifier(item);

  comparePeriod = (o1: IPeriod | null, o2: IPeriod | null): boolean =>
    this.periodService.compareEntity(o1, o2);

  comparePeriodVersion = (o1: IPeriodVersion | null, o2: IPeriodVersion | null): boolean =>
    this.periodVersionService.compareEntity(o1, o2);
    
  compareOrganization = (o1: IOrganization | null, o2: IOrganization | null): boolean =>
    this.organizationService.compareEntity(o1, o2);
  
  compareScope = (o1: IVariableScope | null, o2: IVariableScope | null): boolean =>
    this.variableScopeService.compareEntity(o1, o2);

  compareCategory = (o1: IVariableCategory | null, o2: IVariableCategory | null): boolean =>
    this.variableCategoryService.compareEntity(o1, o2);
    
  ngOnInit(): void {
    // Get the currently selected ORGANIZATION, by ResilientEnvironmentService
    this.subscriptionWorkOrganization = this
      .envService
      .selectedWorkOrganization
      /* .pipe(first())		// READS the Observable only once. Doesn't react to later changes on the subscription */
      .subscribe(org => {
        if(org){
          this.selectedOrganization = org;
          
          // Set filter
          this.searchForm.get('ownerId')?.setValue(org.id);
          
          // Clear list
          this.outputData = undefined;

          // Execute search
          if (this.searchForm.valid) {
            this.load();
          }
        }
      });
      
    // Listen to changes to the scope filter property
    this.searchForm.get('variableScopeId')?.valueChanges.subscribe((variableScopeId) => {
      this.loadCategoriesOfSelectedScope(variableScopeId);
      this.searchForm.get('variableCategoryId')?.setValue(null);
    });
      
    // Listen to changes to the period filter property
    this.searchForm.get('periodId')?.valueChanges.subscribe((periodId) => {
      this.loadPeriodVersionsOfSelectedPeriod(periodId);
      this.searchForm.get('periodVersionId')?.setValue(null);
    });
    
    this.loadRelationshipsOptions().subscribe(() => {    
      this.subscription = combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data])
      .pipe(
        tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)),
        tap(() => {
          if (!this.outputData || this.outputData.length === 0) {
            this.load();
          }
        }),
      )
      .subscribe();
    });	
  }
  
  onSearch(): void {
	  this.load();
  }
  
  load(): void {
    this.queryBackend().subscribe({
      next: (res: EntityArrayResponseType) => {
        this.onResponseSuccess(res);
      },
    });
  }

  navigateToWithComponentValues(event: SortState): void {
    this.handleNavigation(event);
  }

  protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void {
    this.sortState.set(this.sortService.parseSortParam(params.get(SORT) ?? data[DEFAULT_SORT_DATA]));
  }

  protected onResponseSuccess(response: EntityArrayResponseType): void {
    const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body);
    /* this.outputData = this.refineData(dataFromBody); */
    this.outputData = dataFromBody;

    // Allow Angular to process UI updates before turning off loading
    this.ngZone.runOutsideAngular(() => {
        setTimeout(() => {
            this.ngZone.run(() => {
                this.isLoading = false;
            });
        }, 0);
    });
  }

  protected refineData(data: IOutputData[]): IOutputData[] {
    const { predicate, order } = this.sortState();
    return predicate && order ? data.sort(this.sortService.startSort({ predicate, order })) : data;
  }

  protected fillComponentAttributesFromResponseBody(data: IOutputData[] | null): IOutputData[] {
    return data ?? [];
  }

  protected queryBackend(): Observable<EntityArrayResponseType> {
    this.isLoading = true;
    const queryObject: any = {
      eagerload: true,
      sort: this.sortService.buildSortParam(this.sortState()),
    };
    
    const searchModel = this.outputDataSearchFormService.getOutputData(this.searchForm);
    this.outputDataSearchFormService.applySearchConditionsToQueryObject(queryObject, searchModel);
    
    return this.outputDataService.queryByCriteria(queryObject)
            .pipe( 
              tap(() => {
                // this.dataLoaded = true;
              })
            );
  }

  protected handleNavigation(sortState: SortState): void {
    const queryParamsObj = {
      sort: this.sortService.buildSortParam(sortState),
    };

    this.ngZone.run(() => {
      this.router.navigate(['./'], {
        relativeTo: this.activatedRoute,
        queryParams: queryParamsObj,
      });
    });
  }

  onPeriodChange(event: Event) {
    const selectedValue = (event.target as HTMLSelectElement).value;
    this.searchForm.get('periodId')?.setValue(Number(selectedValue));
  }
  
  protected loadRelationshipsOptions(): Observable<void> {
    return new Observable<void>((observer) => {
      this.periodService
      .query()
      .pipe(map((res: HttpResponse<IPeriod[]>) => res.body ?? []))
      .subscribe((periods: IPeriod[]) => { 
        this.periodsSharedCollection = periods;
        
        // Set first Period as default. It's mandatory to select a Period
        const firstPeriodId = this.periodsSharedCollection[0]?.id;
        this.searchForm.get('periodId')?.setValue(firstPeriodId);

        //Load the PeriodVersion list
        this.loadPeriodVersionsOfSelectedPeriod(firstPeriodId);
        
        observer.next(); // Notify that process is complete
        observer.complete();
      });

      this.variableScopeService
        .queryForData()
        .pipe(map((res: HttpResponse<IVariableScope[]>) => res.body ?? []))
        .subscribe((scopes: IVariableScope[]) => (this.scopesSharedCollection = scopes));
      
      
      this.organizationService
        .query()
        .pipe(map((res: HttpResponse<IOrganization[]>) => res.body ?? []))
        .subscribe((orgs: IOrganization[]) => (this.orgsSharedCollection = orgs));
  
    });
  }
  
  protected loadCategoriesOfSelectedScope(variableScopeId: number|null|undefined): void {
    if (variableScopeId) {
      this.variableCategoryService
          .queryByScope(variableScopeId)
          .pipe(map((res: HttpResponse<IVariableCategory[]>) => res.body ?? []))
          .subscribe((cats: IVariableCategory[]) => (this.categoriesSharedCollection = cats));
    } else {
      this.categoriesSharedCollection = [];
    }
  }
  
  protected loadPeriodVersionsOfSelectedPeriod(periodId: number|null|undefined): void {
    if (periodId) {
      this.periodService
          .queryVersions(periodId)
          .pipe(map((res: HttpResponse<IPeriodVersion[]>) => res.body ?? []))
          .subscribe((versions: IPeriodVersion[]) => {
            this.periodVersionsSharedCollection = versions;
            
            // Set first PeriodVersion as default. It's mandatory to select a PeriodVersion
            const firstPeriodVersionId = this.periodVersionsSharedCollection[0]?.id;
            this.searchForm.get('periodVersionId')?.setValue(firstPeriodVersionId);
          });
    } else {
      this.periodVersionsSharedCollection = [];
    }
  }
  
  export(): void {
    this.isExporting = true;
    const periodVersionId = this.searchForm.get('periodVersionId')?.value;

    if (periodVersionId) {
      this.outputDataService.export(periodVersionId).subscribe((blob: Blob) => {
        const a = document.createElement('a');
        const objectUrl = URL.createObjectURL(blob);
        a.href = objectUrl;
        a.download = 'report.xlsx';
        a.click();
        URL.revokeObjectURL(objectUrl);

        this.isExporting = false;
      });
    } else {
      this.isExporting = false;
    }
  }
}
