import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewEncapsulation, Input, Output, EventEmitter, QueryList, ViewChildren, inject } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { tap, take, map, catchError, switchMap, shareReplay, filter } from 'rxjs/operators';
import { of, BehaviorSubject, Subject, combineLatest } from 'rxjs';
import { ReportFilterService, ReportFilter, EditorType } from './report-filter.service';
import { MessageService, promptAction, PromptSettings } from '../_shared/services/message.service';
import { FormBuilder, FormsModule } from '@angular/forms';
import dayjs from 'dayjs';
import { FilterValue } from '../filter-controls/control-base.component';
import { DatasourceBaseComponent } from '../report-datasources/datasource-base.component';
import * as util from '../_shared/utils/util';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../app.config';
import { DatasourceTestComponent } from "../report-datasources/datasource-_test/datasource-test.component";
import { DatasourceCreditLimitsComponent } from "../report-datasources/datasource-credit-limits/datasource-credit-limits.component";
import { DatasourceDailyIndexPricesComponent } from "../report-datasources/datasource-daily-index-prices/datasource-daily-index-prices.component";
import { DatasourceCounterpartyDataComponent } from "../report-datasources/datasource-counterparty-data/datasource-counterparty-data.component";
import { DatasourceForwardIndexPricesComponent } from "../report-datasources/datasource-forward-index-prices/datasource-forward-index-prices.component";
import { DatasourceKeepWholesComponent } from "../report-datasources/datasource-keep-wholes/datasource-keep-wholes.component";
import { DatasourceMonthlyIndexPricesComponent } from "../report-datasources/datasource-monthly-index-prices/datasource-monthly-index-prices.component";
import { DatasourceNominationsComponent } from "../report-datasources/datasource-nominations/datasource-nominations.component";
import { DatasourcePlantStatementsComponent } from "../report-datasources/datasource-plant-statements/datasource-plant-statements.component";
import { DatasourcePtrDataComponent } from "../report-datasources/datasource-ptr-data/datasource-ptr-data.component";
import { DatasourceSchedulingDataComponent } from "../report-datasources/datasource-scheduling-data/datasource-scheduling-data.component";
import { DatasourceSosDataComponent } from "../report-datasources/datasource-sos-data/datasource-sos-data.component";
import { DatasourceTradesComponent } from "../report-datasources/datasource-trades/datasource-trades.component";
import { DatasourceValuationDataComponent } from "../report-datasources/datasource-valuation-data/datasource-valuation-data.component";
import { DatasourceValuationDataByDealComponent } from "../report-datasources/datasource-valuation-data-by-deal/datasource-valuation-data-by-deal.component";
import { DatasourceWaspDataComponent } from "../report-datasources/datasource-wasp-data/datasource-wasp-data.component";
import { DatasourceMeterDataComponent } from "../report-datasources/datasource-meter-data/datasource-meter-data.component";
import { DatasourceValuationDataByPathComponent } from '../report-datasources/datasource-valuation-data-by-path/datasource-valuation-data-by-path.component';
import { ToastService } from '../_shared/services/fast-toast.service';

@Component({
  selector: 'report-filter',
  templateUrl: './report-filter.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  imports: [FAST_PAGE_COMMON, FAST_KENDO_COMMON, FormsModule,
    DatasourceTestComponent,
    DatasourceCreditLimitsComponent,
    DatasourceDailyIndexPricesComponent,
    DatasourceCounterpartyDataComponent,
    DatasourceForwardIndexPricesComponent,
    DatasourceKeepWholesComponent,
    DatasourceMonthlyIndexPricesComponent,
    DatasourceNominationsComponent,
    DatasourcePlantStatementsComponent,
    DatasourcePtrDataComponent,
    DatasourceSchedulingDataComponent,
    DatasourceSosDataComponent,
    DatasourceTradesComponent,
    DatasourceValuationDataComponent,
    DatasourceValuationDataByDealComponent,
    DatasourceValuationDataByPathComponent,
    DatasourceWaspDataComponent,
    DatasourceMeterDataComponent
  ]
})
export class ReportFilterComponent {
  @ViewChildren('childDataSource') allChildDataSources: QueryList<DatasourceBaseComponent>;
  @Input() reportId: number;
  @Input() hasReportModifyPermission: boolean
  @Output() closed = new EventEmitter()

  private messageService = inject(MessageService);
  private titleService = inject(Title);
  private filterService = inject(ReportFilterService);
  private fb = inject(FormBuilder);
  private ref = inject(ChangeDetectorRef);
  private toast = inject(ToastService);

  util = util;
  selectedFilter: ReportFilter;
  editorOpened: boolean = false;
  newFilterName: string;
  selectedDataSource: string;
  expanded: boolean = true;
  editorType: EditorType;
  typeEnum = EditorType;

  localDataSourcesById: Record<number, string> = {}

  loading$ = new BehaviorSubject<boolean>(true);

  refreshFilters$ = new BehaviorSubject<number>(null)
  refreshDataSources$ = new BehaviorSubject(null)
  filterFilters$ = new BehaviorSubject<string>(null)

  filterSelected$ = new Subject<ReportFilter>()

  saveFilter$ = new Subject()
  addFilter$ = new Subject<string>()
  renameFilter$ = new Subject<string>()
  copyFilter$ = new Subject<string>()
  deleteFilter$ = new Subject()

  tabTextByDataSourceId: { [sourceId: number]: string } = { 0: '' }

  filters$ = this.refreshFilters$.pipe(
    tap(() => {
      this.loading$.next(true);
    }),
    switchMap((addedFilterId) => {
      return combineLatest([of(addedFilterId), this.filterService.getFilters(this.reportId)]);
    }),
    map(([selectedFilterId, filters]) => {
      if (selectedFilterId) {
        this.selectedFilter = filters.find(x => x.id === selectedFilterId);
        this.filterSelected$.next(this.selectedFilter);
      }
      else if (filters?.length > 0) {
        this.selectedFilter = filters.find(x => x.isSelected === true) ?? null;
        this.filterSelected$.next(this.selectedFilter);
      }
      else {
        this.selectedFilter = null;
        this.filterSelected$.next(this.selectedFilter);
      }

      this.loading$.next(false);
      return filters;
    }),
    shareReplay(1),
    catchError(err => {
      return util.handleError(err, this.messageService)
    })
  );

  dataSources$ = this.refreshDataSources$.pipe(
    tap(() => {
      this.loading$.next(true);
    }),
    switchMap(() => {
      return this.filterService.getDataSources(this.reportId);
    }),
    map((dataSources) => {
      dataSources.forEach(dataSource => {
        this.localDataSourcesById[dataSource.id] = dataSource.name;
      });
      this.loading$.next(false);
      return dataSources;
    }),
    tap(() => {

    }),
    shareReplay(1),
    catchError(err => {
      return util.handleError(err, this.messageService)
    })
  )

  filterParams$ = combineLatest([this.dataSources$, this.filterSelected$]).pipe(
    filter(([, filterSelected]) => {
      return filterSelected !== null && filterSelected.id !== 0;
    }),
    tap(() => {
      this.loading$.next(true);
    }),
    switchMap(([, filterSelected]) => {
      return this.filterService.getFilterParams(filterSelected.id);
    }),
    map((filterParams: FilterValue[]) => {
      this.allChildDataSources.forEach(dataSource => {
        dataSource.setFilterValues(filterParams);
      })
      this.loading$.next(false);
      return filterParams;
    })
  )

  filtersFiltered$ = this.filterFilters$.pipe(util.filterIdNames(this.loading$, this.filters$, null));

  addFilterResult$ = this.addFilter$.pipe(
    switchMap(newFilterName => {
      this.filterFilters$.next(null);
      return this.filterService.addFilter(this.reportId, newFilterName);
    }),
    tap(addedFilterId => {
      this.toast.success('filter added');
      this.loading$.next(false);
      this.editorOpened = false;
      this.refreshFilters$.next(addedFilterId);
    }),
    catchError(err => {
      return util.handleError(err, this.messageService)
    })
  )

  renameFilterResult$ = this.renameFilter$.pipe(
    switchMap((newFilterName) => {
      return this.filterService.renameFilter(this.selectedFilter.id, newFilterName);
    }),
    tap((newFilterName) => {
      this.selectedFilter.name = newFilterName;
      const filterInput = document.querySelector('.filterSelection .k-input') as HTMLInputElement;
      filterInput.value = newFilterName;
      this.toast.success('filter renamed');
      this.loading$.next(false);
      this.editorOpened = false;
    }),
    catchError(err => {
      return util.handleError(err, this.messageService)
    })
  )

  copyFilterResult$ = this.copyFilter$.pipe(
    switchMap((newFilterName) => {
      return this.filterService.copyFilter(this.selectedFilter.id, newFilterName);
    }),
    tap((copiedFilterId) => {
      this.toast.success('filter copied');
      this.loading$.next(false);
      this.editorOpened = false;
      this.refreshFilters$.next(copiedFilterId);
    }),
    catchError(err => {
      return util.handleError(err, this.messageService)
    })
  )

  deleteFilterResult$ = this.deleteFilter$.pipe(
    switchMap(() => {
      return this.filterService.deleteFilter(this.selectedFilter.id);
    }),
    tap(() => {
      this.toast.success('delete successful');
      this.loading$.next(false);
      this.editorOpened = false;
      this.refreshFilters$.next(null);
    }),
    catchError(err => {
      return util.handleError(err, this.messageService)
    })
  )

  saveFilter(): void {
    this.loading$.next(true);
    let areAllChildDataSourcesValid: boolean = true;
    this.allChildDataSources.forEach(dataSource => {
      if (!dataSource.isValid)
        areAllChildDataSourcesValid = false;
    })
    if (areAllChildDataSourcesValid) {
      let allValues: FilterValue[] = [];
      this.allChildDataSources.forEach(childDataSource => {
        const values: FilterValue[] = childDataSource.getFilterValues();
        allValues = allValues.concat(values);
      })
      allValues = allValues.filter(x => x.value1 !== null);

      const invalidValues = allValues.filter(x =>
        x.value1?.includes('NaN') ||
        x.preview?.includes('Not Found')
      );

      if (invalidValues.length > 0) {
        this.loading$.next(false);
        const invalidNames = invalidValues.map(x => x.name).join(', ');
        this.toast.error(`Cannot save filter: ${invalidNames} data is still loading. Please wait a moment and try again.`);
        return;
      }

      this.filterService.saveFilter(this.reportId, this.selectedFilter?.id, allValues).pipe(
        take(1),
        tap(() => {
          this.loading$.next(false);
          this.closed.emit();
          this.toast.success('filter saved');
        }),
        catchError(err => {
          return util.handleError(err, this.messageService)
        }),
      ).subscribe();
    }
    else {
      this.loading$.next(false);
      this.toast.error("validation failed");
    }
  }

  closeFilter(): void {
    this.closed.emit();
  }

  openEditor(editorType: EditorType): void {
    this.editorType = editorType;

    if (this.editorType == EditorType.AddNew) {
      this.newFilterName = 'New Filter ' + dayjs().format('YYYY-MM-DD h:mm:ss a');
    }
    else if (this.editorType == EditorType.Rename) {
      this.newFilterName = this.selectedFilter.name;
    }
    else if (this.editorType == EditorType.Copy) {
      this.newFilterName = this.selectedFilter.name + " - Copy";
    }

    this.editorOpened = true;
  }

  addRenameOrCopyFilter() {
    if (this.editorType == EditorType.AddNew)
      this.addFilter();
    else if (this.editorType == EditorType.Rename)
      this.renameFilter();
    else if (this.editorType == EditorType.Copy)
      this.copyFilter();
  }

  addFilter(): void {
    if (!util.isNullOrWhitespace(this.newFilterName)) {
      this.loading$.next(true);
      this.addFilter$.next(this.newFilterName);
    } else
      this.toast.error("validation failed");
  }

  renameFilter(): void {
    this.loading$.next(true);
    this.renameFilter$.next(this.newFilterName);
  }

  copyFilter(): void {
    this.loading$.next(true);
    this.copyFilter$.next(this.newFilterName);
  }

  log() {
    console.log('true')
  }

  deleteFilter(): void {
    const ps: PromptSettings = {
      title: "Please Confirm",
      content: "Are you sure you want to delete this filter?",
      type: 'Yes-No'
    }

    this.messageService.prompt(ps).then((result) => {
      if (result === promptAction.Yes) {
        this.loading$.next(true);
        this.deleteFilter$.next(null);
      }
    });
  }

  filterFilters(value: string) {
    this.filterFilters$.next(value);
  }

  onFilterSelected(reportFilter: ReportFilter) {
    this.selectedFilter = reportFilter ?? null;
    this.ref.detectChanges(); //wait for allChildDataSources QueryList to update from the DOM
    this.filterSelected$.next(this.selectedFilter);
  }

  hasDataSourceId(dataSources: util.IdName[], id: number): boolean {
    return dataSources.some(x => x.id === id);
  }

  valueUpdated(dataSourceId: number) {
    let hasValueCount: number = 0;
    if (this.allChildDataSources && this.localDataSourcesById) {
      const childComponent = this.allChildDataSources.find(child => dataSourceId === child.dataSourceId);
      if (childComponent)
        hasValueCount = childComponent.hasValueCount;

      const dataSourceName = this.localDataSourcesById[dataSourceId];
      this.tabTextByDataSourceId[dataSourceId] = dataSourceName + ' (' + hasValueCount + ')';
    }
  }

  getEditorTitle() {
    let title: string = "";
    if (this.editorType == EditorType.AddNew)
      title = "Add New Filter";
    else if (this.editorType == EditorType.Rename)
      title = "Rename Filter";
    else if (this.editorType == EditorType.Copy)
      title = "Copy Saved Filter";

    return title;
  }
}
