import { Component, ChangeDetectionStrategy, OnInit, input, output, ViewEncapsulation, inject } from '@angular/core';
import { tap, map, catchError, switchMap, shareReplay, retry } from 'rxjs/operators';
import { BehaviorSubject, Subject } from 'rxjs';
import { MessageService } from '../_shared/services/message.service';
import { CustomFormBuilder } from '../_shared/services/custom-form-builder.service';
import { NotifyService } from '../_shared/services/notify.service';
import * as util from '../_shared/utils/util';
import { ActualsCrudeHistoryService, HistoryArray, HistoryItem, RequiredData } from './actuals-crude-history.service';
import { SaveType } from '../_shared/utils/util';
import { UntypedFormArray, Validators } from '@angular/forms';
import dayjs from 'dayjs';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../app.config';

@Component({
  selector: 'app-actuals-crude-history',
  templateUrl: './actuals-crude-history.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  standalone: true,
  imports: [FAST_PAGE_COMMON, FAST_KENDO_COMMON]
})
export class ActualsCrudeHistoryComponent implements OnInit {
  actualTypeId = input.required<number>();
  supplyNomId = input.required<number>();
  marketNomId = input.required<number>();
  lastTransferId = input.required<number>();
  closeClicked = output();
  refreshGridItems = output();

  service = inject(ActualsCrudeHistoryService);
  messageService = inject(MessageService);
  fb = inject(CustomFormBuilder);
  notify = inject(NotifyService);

  constructor() {

  }

  util = util;
  localRequiredData: RequiredData;
  hasModifyPermission = false;
  detailOpened$ = new BehaviorSubject<boolean>(false)
  refreshRequiredData$ = new BehaviorSubject(null)
  detailLoading$ = new BehaviorSubject<boolean>(true)
  refreshDetail$ = new BehaviorSubject<boolean>(false)
  save$ = new Subject<SaveType>()
  historyArrayForm = this.getHistoryArrayFormGroup()
  historyItemsFormArray: UntypedFormArray;
  windowWidth = window.innerWidth;

  ngOnInit() {
    this.refreshDetail$.next(true);
  }

  getHistoryArrayFormGroup() {
    const fb = this.fb;
    const fg: util.FormModel<HistoryArray> = fb.group({
      historyItems: fb.arr([])
    });
    return fg;
  }

  getHistoryItemFormGroup(item: HistoryItem) {
    const fb = this.fb;
    const fg: util.FormModel<HistoryItem> = fb.group({
      actualVolume: fb.ctr(item.actualVolume),
      price: fb.ctr(item.price),
      priceAdj: fb.ctr(item.priceAdj),
      actualFee: fb.ctr(item.actualFee),
      adjustment: fb.ctr(item.adjustment),
      transportRate: fb.ctr(item.transportRate),
      isVolumeEdited: fb.ctr(item.isVolumeEdited),
      isPriceEdited: fb.ctr(item.isPriceEdited),
      isPriceAdjEdited: fb.ctr(item.isPriceAdjEdited),
      isActualFeeEdited: fb.ctr(item.isActualFeeEdited),
      isAdjustmentEdited: fb.ctr(item.isAdjustmentEdited),
      isTransportRateEdited: fb.ctr(item.isTransportRateEdited),
      saveDate: fb.ctr(item.saveDate, [Validators.required]),
      supplyNomId: fb.ctr(item.supplyNomId),
      marketNomId: fb.ctr(item.marketNomId),
      lastTransferId: fb.ctr(item.lastTransferId)
    });
    return fg;
  }

  saveHistory(instance: ActualsCrudeHistoryComponent, saveType: util.SaveType): void {
    instance.historyArrayForm.markAllAsTouched();
    const saveDates = this.historyItemsFormArray.controls.map(control => control.value.saveDate);
    const saveDatesWithoutTime = saveDates.map(date => dayjs(date).format('YYYY-MM-DD'));
    const hasDuplicateSaveDates = saveDatesWithoutTime.some((date, index) => saveDatesWithoutTime.indexOf(date) !== index);
    if (instance.historyArrayForm.valid && !hasDuplicateSaveDates)
      instance.save$.next(saveType);
    else if (hasDuplicateSaveDates)
      instance.notify.error('duplicate save dates');
    else
      instance.notify.error('validation failed');
  }

  closeHistory(): void {
    this.closeClicked.emit();
  }

  addHistoryItem() {
    this.historyItemsFormArray.insert(0, this.getHistoryItemFormGroup({
      saveDate: dayjs().startOf('day').toDate(),
      actualVolume: null,
      price: null,
      priceAdj: null,
      actualFee: null,
      adjustment: null,
      transportRate: null,
      isVolumeEdited: false,
      isPriceEdited: false,
      isPriceAdjEdited: false,
      isActualFeeEdited: false,
      isAdjustmentEdited: false,
      isTransportRateEdited: false,
      supplyNomId: this.supplyNomId(),
      marketNomId: this.marketNomId(),
      lastTransferId: this.lastTransferId()
    }));
    this.historyItemsFormArray.markAsDirty();
  }

  removeHistoryItem(idx: number) {
    this.historyItemsFormArray.removeAt(idx);
    this.historyItemsFormArray.markAsDirty();
  }

  detail$ = this.refreshDetail$.pipe(
    switchMap(() => {
      return this.service.getHistoryItems(this.actualTypeId(), this.supplyNomId(), this.marketNomId(), this.lastTransferId());
    }),
    map(detail => {
      util.convertToDates(detail, []);
      this.historyItemsFormArray = this.historyArrayForm.get('historyItems') as UntypedFormArray;
      this.historyItemsFormArray.clear();
      if (detail.length === 0) {
        this.addHistoryItem();
      }
      else {
        detail.forEach(item => {
          this.historyItemsFormArray.push(this.getHistoryItemFormGroup(item));
        });
        this.historyArrayForm.patchValue({ historyItems: detail });
      }
    }),
    tap(() => {
      this.detailLoading$.next(false);
    }),
    shareReplay(1),
    catchError(err => {
      return util.handleError(err, this.messageService)
    }), retry(3)
  )

  saveResult$ = this.save$.pipe(
    switchMap(() => {
      this.detailLoading$.next(true);
      const itemsToSave = this.historyItemsFormArray.value as HistoryItem[];
      return this.service.saveHistoryItems(this.actualTypeId(), this.supplyNomId(), this.marketNomId(), this.lastTransferId(), itemsToSave);
    }),
    tap(() => {
      this.notify.success('save successful');
      this.refreshDetail$.next(true);
      this.refreshGridItems.emit();
      this.closeHistory();
    }),
    shareReplay(1),
    catchError(err => {
      this.detailLoading$.next(false);
      return util.handleError(err, this.messageService)
    }), retry(3)
  )
}
