import {
  AsyncPipe,
  NgForOf,
  NgIf,
} from '@angular/common';
import {
  Component,
  OnInit,
} from '@angular/core';
import {
  ActivatedRoute,
  Data,
  Router,
  RouterLink,
} from '@angular/router';
import {
  TranslateModule,
  TranslateService,
} from '@ngx-translate/core';
import {
  BehaviorSubject,
  combineLatest,
  Observable,
} from 'rxjs';
import {
  distinctUntilChanged,
  map,
  switchMap,
  tap,
} from 'rxjs/operators';

import { AuthService } from '../core/auth/auth.service';
import {
  SortDirection,
  SortOptions,
} from '../core/cache/models/sort-options.model';
import { FindListOptions } from '../core/data/find-list-options.model';
import { PaginatedList } from '../core/data/paginated-list.model';
import { RemoteData } from '../core/data/remote-data';
import { Suggestion } from '../core/notifications/suggestions/models/suggestion.model';
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
import { PaginationService } from '../core/pagination/pagination.service';
import { redirectOn4xx } from '../core/shared/authorized.operators';
import {
  getFirstCompletedRemoteData,
  getFirstSucceededRemoteDataPayload,
} from '../core/shared/operators';
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
import { SuggestionActionsComponent } from '../notifications/suggestion-actions/suggestion-actions.component';
import { SuggestionApproveAndImport } from '../notifications/suggestion-list-element/suggestion-approve-and-import';
import { SuggestionListElementComponent } from '../notifications/suggestion-list-element/suggestion-list-element.component';
import { SuggestionTargetsStateService } from '../notifications/suggestion-targets/suggestion-targets.state.service';
import {
  SuggestionBulkResult,
  SuggestionsService,
} from '../notifications/suggestions.service';
import { AlertComponent } from '../shared/alert/alert.component';
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
import { NotificationsService } from '../shared/notifications/notifications.service';
import { PaginationComponent } from '../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { VarDirective } from '../shared/utils/var.directive';
import { getWorkspaceItemEditRoute } from '../workflowitems-edit-page/workflowitems-edit-page-routing-paths';

@Component({
  selector: 'ds-suggestion-page',
  templateUrl: './suggestions-page.component.html',
  styleUrls: ['./suggestions-page.component.scss'],
  imports: [
    AsyncPipe,
    VarDirective,
    NgIf,
    RouterLink,
    TranslateModule,
    SuggestionActionsComponent,
    ThemedLoadingComponent,
    PaginationComponent,
    SuggestionListElementComponent,
    NgForOf,
    AlertComponent,
  ],
  standalone: true,
})

/**
 * Component used to visualize one of the suggestions from the publication claim page or from the notification pop up
 */

export class SuggestionsPageComponent implements OnInit {

  /**
   * The pagination configuration
   */
  paginationOptions: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
    id: 'sp',
    pageSizeOptions: [5, 10, 20, 40, 60],
  });

  /**
   * The sorting configuration
   */
  paginationSortConfig: SortOptions = new SortOptions('trust', SortDirection.DESC);

  /**
   * The FindListOptions object
   */
  defaultConfig: FindListOptions = Object.assign(new FindListOptions(), { sort: this.paginationSortConfig });

  /**
   * A boolean representing if results are loading
   */
  public processing$ = new BehaviorSubject<boolean>(false);

  /**
   * A list of remote data objects of suggestions
   */
  suggestionsRD$: BehaviorSubject<PaginatedList<Suggestion>> = new BehaviorSubject<PaginatedList<Suggestion>>({} as any);

  targetRD$: Observable<RemoteData<SuggestionTarget>>;
  targetId$: Observable<string>;

  suggestionTarget: SuggestionTarget;
  suggestionId: any;
  suggestionSource: any;
  researcherName: any;
  researcherUuid: any;

  selectedSuggestions: { [id: string]: Suggestion } = {};
  isBulkOperationPending = false;

  constructor(
    private authService: AuthService,
    private notificationService: NotificationsService,
    private paginationService: PaginationService,
    private route: ActivatedRoute,
    private router: Router,
    private suggestionService: SuggestionsService,
    private suggestionTargetsStateService: SuggestionTargetsStateService,
    private translateService: TranslateService,
    private workspaceItemService: WorkspaceitemDataService,
  ) {
  }

  ngOnInit(): void {
    this.targetRD$ = this.route.data.pipe(
      map((data: Data) => data.suggestionTargets as RemoteData<SuggestionTarget>),
      redirectOn4xx(this.router, this.authService),
    );

    this.targetId$ = this.targetRD$.pipe(
      getFirstSucceededRemoteDataPayload(),
      map((target: SuggestionTarget) => target.id),
    );
    this.targetRD$.pipe(
      getFirstSucceededRemoteDataPayload(),
      tap((suggestionTarget: SuggestionTarget) => {
        this.suggestionTarget = suggestionTarget;
        this.suggestionId = suggestionTarget.id;
        this.researcherName = suggestionTarget.display;
        this.suggestionSource = suggestionTarget.source;
        this.researcherUuid = this.suggestionService.getTargetUuid(suggestionTarget);
      }),
      switchMap(() => this.updatePage()),
    ).subscribe();

    this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction();
  }

  /**
   * Called when one of the pagination settings is changed
   */
  onPaginationChange() {
    this.updatePage().subscribe();
  }

  /**
   * Update the list of suggestions
   */
  updatePage(): Observable<RemoteData<PaginatedList<Suggestion>>> {
    this.processing$.next(true);
    const pageConfig$: Observable<FindListOptions> = this.paginationService.getFindListOptions(
      this.paginationOptions.id,
      this.defaultConfig,
    ).pipe(
      distinctUntilChanged(),
    );

    return combineLatest([this.targetId$, pageConfig$]).pipe(
      switchMap(([targetId, config]: [string, FindListOptions]) => {
        return this.suggestionService.getSuggestions(
          targetId,
          config.elementsPerPage,
          config.currentPage,
          config.sort,
        );
      }),
      getFirstCompletedRemoteData(),
      tap((resultsRD: RemoteData<PaginatedList<Suggestion>>) => {
        this.processing$.next(false);
        if (resultsRD.hasSucceeded) {
          this.suggestionsRD$.next(resultsRD.payload);
        } else {
          this.suggestionsRD$.next(null);
        }

        this.suggestionService.clearSuggestionRequests();
      }),
    );
  }

  /**
   * Used to delete a suggestion.
   * @suggestionId
   */
  ignoreSuggestion(suggestionId) {
    this.suggestionService.ignoreSuggestion(suggestionId).pipe(
      tap(() => this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction()),
      switchMap(() => this.updatePage()),
    ).subscribe();
  }

  /**
   * Used to delete all selected suggestions.
   */
  ignoreSuggestionAllSelected() {
    this.isBulkOperationPending = true;
    this.suggestionService.ignoreSuggestionMultiple(Object.values(this.selectedSuggestions)).pipe(
      tap((results: SuggestionBulkResult) => {
        this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
        this.isBulkOperationPending = false;
        this.selectedSuggestions = {};
        if (results.success > 0) {
          this.notificationService.success(
            this.translateService.get('suggestion.ignoreSuggestion.bulk.success',
              { count: results.success }));
        }
        if (results.fails > 0) {
          this.notificationService.error(
            this.translateService.get('suggestion.ignoreSuggestion.bulk.error',
              { count: results.fails }));
        }
      }),
      switchMap(() => this.updatePage()),
    ).subscribe();
  }

  /**
   * Used to approve & import.
   * @param event contains the suggestion and the target collection
   */
  approveAndImport(event: SuggestionApproveAndImport) {
    this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId).pipe(
      tap((workspaceitem: WorkspaceItem) => {
        const content = this.translateService.instant('suggestion.approveAndImport.success', { url: getWorkspaceItemEditRoute(workspaceitem.id) });
        this.notificationService.success('', content, { timeOut:0 }, true);
        this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
      }),
      switchMap(() => this.updatePage()),
    ).subscribe();
  }

  /**
   * Used to approve & import all selected suggestions.
   * @param event contains the target collection
   */
  approveAndImportAllSelected(event: SuggestionApproveAndImport) {
    this.isBulkOperationPending = true;
    this.suggestionService.approveAndImportMultiple(this.workspaceItemService, Object.values(this.selectedSuggestions), event.collectionId).pipe(
      tap((results: SuggestionBulkResult) => {
        this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
        this.isBulkOperationPending = false;
        this.selectedSuggestions = {};
        if (results.success > 0) {
          this.notificationService.success(
            this.translateService.get('suggestion.approveAndImport.bulk.success',
              { count: results.success }));
        }
        if (results.fails > 0) {
          this.notificationService.error(
            this.translateService.get('suggestion.approveAndImport.bulk.error',
              { count: results.fails }));
        }
      }),
      switchMap(() => this.updatePage()),
    ).subscribe();
  }

  /**
   * When a specific suggestion is selected.
   * @param object the suggestions
   * @param selected the new selected value for the suggestion
   */
  onSelected(object: Suggestion, selected: boolean) {
    if (selected) {
      this.selectedSuggestions[object.id] = object;
    } else {
      delete this.selectedSuggestions[object.id];
    }
  }

  /**
   * When Toggle Select All occurs.
   * @param suggestions all the visible suggestions inside the page
   */
  onToggleSelectAll(suggestions: Suggestion[]) {
    if ( this.getSelectedSuggestionsCount() > 0) {
      this.selectedSuggestions = {};
    } else {
      suggestions.forEach((suggestion) => {
        this.selectedSuggestions[suggestion.id] = suggestion;
      });
    }
  }

  /**
   * The current number of selected suggestions.
   */
  getSelectedSuggestionsCount(): number {
    return Object.keys(this.selectedSuggestions).length;
  }

  /**
   * Return true if all the suggestion are configured with the same fixed collection in the configuration.
   * @param suggestions
   */
  isCollectionFixed(suggestions: Suggestion[]): boolean {
    return this.suggestionService.isCollectionFixed(suggestions);
  }

  /**
   * Label to be used to translate the suggestion source.
   */
  translateSuggestionSource() {
    return this.suggestionService.translateSuggestionSource(this.suggestionSource);
  }

  /**
   * Label to be used to translate the suggestion type.
   */
  translateSuggestionType() {
    return this.suggestionService.translateSuggestionType(this.suggestionSource);
  }

}