import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, ElementRef, ViewEncapsulation, HostListener, inject } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { tap, map, catchError, switchMap, filter, shareReplay, retry } from 'rxjs/operators';
import { of, BehaviorSubject, Subject, combineLatest, Observable } from 'rxjs';
import { State } from '@progress/kendo-data-query';
import { MessageService, promptAction } from '../_shared/services/message.service';
import { Validators } from '@angular/forms';
import { CustomFormBuilder } from '../_shared/services/custom-form-builder.service';
import { NotifyService } from '../_shared/services/notify.service';
import * as util from '../_shared/utils/util';
import { Detail, Item, SeveranceTaxService, RequiredData } from './severance-tax.service';
import { SaveType } from '../_shared/utils/util';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../app.config';
import { GridDataResult } from '@progress/kendo-angular-grid';

@Component({
  selector: 'app-severance-tax',
  templateUrl: './severance-tax.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
  imports: [FAST_KENDO_COMMON, FAST_PAGE_COMMON]
})
export class SeveranceTaxComponent {
  @ViewChild("grid", { read: ElementRef }) kendoGridEl: ElementRef;
  @HostListener('window:resize') onResize() {
    //this function is empty but for some reason it helps the window to resize faster
  };

  service = inject(SeveranceTaxService);
  messageService = inject(MessageService);
  titleService = inject(Title);
  fb = inject(CustomFormBuilder);
  notify = inject(NotifyService);
  ref = inject(ChangeDetectorRef);

  util = util;
  detailForm = this.getDetailForm();
  gridScrollPosition: util.GridScrollPosition = { topPos: 0, leftPos: 0 };
  localRequiredData: RequiredData;
  hasModifyPermission = false;
  detailInitialValues: Detail = this.detailForm.value as Detail;
  mySelection: number[] = [];
  manualChangesTriggering: boolean = false;
  isAddNew: boolean;
  percentInputFormat = '#,.#####%';
  originalStateId: number;
  // this updates localDetailLoading whenever detailLoading$ changes so that we can check the loading status in this class
  localDetailLoading = true;

  gridLoading$ = new BehaviorSubject<boolean>(true)
  refreshItems$ = new BehaviorSubject<string>(null)
  exporting$ = new BehaviorSubject<boolean>(false)
  detailOpened$ = new BehaviorSubject<boolean>(false)
  refreshRequiredData$ = new BehaviorSubject(null)
  detailLoading$ = new BehaviorSubject<boolean>(true)
  refreshDetail$ = new BehaviorSubject<number>(null)
  saveDialogOpened$ = new BehaviorSubject<boolean>(false)
  filterState$ = new BehaviorSubject<string>(null)
  save$ = new Subject<SaveType>()
  delete$ = new Subject()
  exportClicked$ = new Subject()

  detail$: Observable<Detail>;
  requiredData$: Observable<RequiredData>;
  items$: Observable<GridDataResult>;
  exportAction$: Observable<{ fileBlob: Blob; fileName: string; }>;
  detailLoadingWatcher$: Observable<boolean>;
  saveResult$: Observable<number>;
  deleteResult$: Observable<object>;
  states$: Observable<util.IdName[]>

  state: State = {
    filter: null,
    group: null,
    skip: 0,
    sort: [{ field: 'StateName', dir: 'asc' }],
    take: 50
  };

  constructor() {
    this.detailForm = this.getDetailForm();
    this.detailInitialValues = this.detailForm.getRawValue() as Detail;

    this.requiredData$ = this.refreshRequiredData$.pipe(
      tap(() => this.detailLoading$.next(true)),
      switchMap(refreshType => {
        return combineLatest([this.service.requiredData$, of(refreshType)]);
      }),
      map(([requiredData, refreshType]) => {
        this.localRequiredData = requiredData;
        if (refreshType === util.RefreshType.SelfOnly)
          this.detailLoading$.next(false);
        return requiredData;
      }),
      tap((requiredData) => {
        this.hasModifyPermission = requiredData.hasModifyPermission;
      }),
      shareReplay(1),
      catchError(err => {
        return util.handleError(err, this.messageService)
      }), retry(10)
    )

    this.items$ = this.refreshItems$.pipe(
      tap(() => {
        this.gridLoading$.next(true);
      }),
      switchMap(() => {
        return this.service.getItems(this.state);
      }),
      tap(() => {
        this.gridLoading$.next(false);
        util.goToSavedGridScrollPos(this.kendoGridEl, this.gridScrollPosition);
      }),
      shareReplay(1),
      catchError(err => {
        return util.handleError(err, this.messageService);
      }), retry(10)
    )

    this.exportAction$ = this.exportClicked$.pipe(
      tap(() => {
        this.exporting$.next(true);
      }),
      switchMap(() => {
        return this.service.exportItems(this.state, 'SeveranceTax.xlsx');
      }),
      tap(res => {
        util.openOrSaveFile(res.fileBlob, res.fileName);
        this.exporting$.next(false);
      }),
      shareReplay(1),
      catchError(err => {
        this.exporting$.next(false);
        return util.handleError(err, this.messageService);
      }), retry(10)
    )


    this.detailLoadingWatcher$ = this.detailLoading$.pipe(
      tap(isLoading => {
        this.localDetailLoading = isLoading;
      }),
      shareReplay(1)
    )


    this.detail$ = this.refreshDetail$.pipe(
      filter(id => id !== null),
      switchMap(id => {
        this.detailLoading$.next(true);
        this.detailForm.disable();
        this.detailForm.reset();
        if (id === 0)
          return of(this.detailInitialValues);
        else
          return this.service.getDetail(id);
      }),
      map(result => {
        const detail: Detail = result;
        this.originalStateId = detail.stateId;
        if (detail) {
          util.convertToDates(detail);
          this.detailForm.setValue(detail);
        }
        return detail;
      }),
      tap(() => {
        this.detailFinishedLoading();
      }),
      shareReplay(1),
      catchError(err => {
        this.closeDetail(false)
        return util.handleError(err, this.messageService)
      }), retry(10)
    )

    this.saveResult$ = this.save$.pipe(
      switchMap(SaveType => {
        this.detailLoading$.next(true);
        this.detailForm.disable();
        const itemToSave: Detail = this.detailForm.getRawValue() as Detail;
        return this.service.saveDetail(itemToSave, SaveType, this.originalStateId);
      }),
      tap(saveResult => {
        this.notify.success('save successful');
        this.closeDetail(false);
        this.mySelection = [saveResult];
        this.refreshRequiredData$.next(null);
        this.refreshItems$.next(null);
      }),
      shareReplay(1),
      catchError(err => {
        this.detailFinishedLoading();
        return util.handleError(err, this.messageService)
      }), retry(10)
    )

    this.deleteResult$ = this.delete$.pipe(
      switchMap(() => {
        this.detailLoading$.next(true);
        this.detailForm.disable();
        const itemToDelete: Detail = this.detailForm.getRawValue();
        return this.service.deleteDetail(itemToDelete.stateId);
      }),
      tap(() => {
        this.notify.success('delete successful');
        this.detailFinishedLoading();
        this.closeDetail(false);
        this.refreshItems$.next(null);
      }),
      shareReplay(1),
      catchError(err => {
        return util.handleError(err, this.messageService)
      }), retry(10)
    )
    this.states$ = this.filterState$.pipe(util.filterIdNames(this.detailLoading$, this.requiredData$, 'states'));
  }

  getDetailForm() {
    const fb = this.fb;
    const fg: util.FormModel<Detail> = fb.group({
      stateId: fb.ctr(null, Validators.required),
      gasRate: fb.ctr(null),
      gasRegulatoryFee: fb.ctr(null),
      crudeRate: fb.ctr(null),
      crudeRegulatoryFee: fb.ctr(null),
    });

    return fg;
  }

  openDetail(id: number): void {
    util.saveGridScrollPos(this.kendoGridEl, this.gridScrollPosition);
    this.refreshDetail$.next(id);
    this.detailOpened$.next(true);
  }

  onDetailClosing() {
    util.onDetailChanging(this.detailForm, this.messageService, this.closeDetail, this.save);
  }

  closeDetail = (isFromInterface: boolean) => {
    this.detailOpened$.next(false);
    this.saveDialogOpened$.next(false);

    if (isFromInterface)
      util.goToSavedGridScrollPos(this.kendoGridEl, this.gridScrollPosition);
  }

  detailFinishedLoading(): void {
    this.manualChangesTriggering = true;
    this.detailForm.enable();
    this.detailLoading$.next(false);
  }

  add(): void {
    this.isAddNew = true;
    this.openDetail(0);
  }

  edit(dataItem: Item): void {
    this.isAddNew = false;
    this.openDetail(dataItem.StateId);
  }

  save = (saveType: util.SaveType) => {
    this.detailForm.markAllAsTouched();
    if (this.detailForm.valid)
      this.save$.next(saveType);
    else
      this.notify.error("validation failed");
  }

  delete(): void {
    const message = "Are you sure you want to delete this item?";
    this.messageService.prompt(message, "Please confirm", 'Yes-No').then(result => {
      if (result === promptAction.Yes)
        this.delete$.next(null);
    });
  }

  export(): void {
    this.exportClicked$.next(null);
  }

  dataStateChange(state: State): void {
    this.gridScrollPosition.topPos = 0;
    this.gridScrollPosition.leftPos = 0;
    util.fixKendoQueryFilter(state.filter);
    this.state = state;
    this.refreshItems$.next(null);
  }

  title$ = of('Severance Tax').pipe(
    tap((title) => util.trySetTitle(this.titleService, title))
  )
}
