import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewEncapsulation, Output, EventEmitter, Input, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { tap, map, catchError, switchMap, shareReplay, filter, retry } from 'rxjs/operators';
import { of, BehaviorSubject, Subject, combineLatest, Observable } from 'rxjs';
import { MessageService } from '../../_shared/services/message.service';
import { Validators } from '@angular/forms';
import { DialogService } from '@progress/kendo-angular-dialog';
import { NotifyService } from '../../_shared/services/notify.service';
import * as util from '../../_shared/utils/util';
import { AddDealParams, SaveType, SosDealService } from './sos-deal.service';
import { DrawerItem, DrawerSelectEvent, KENDO_DRAWER } from '@progress/kendo-angular-layout';
import { EntityInfo, PointInfo, SosDeal, SosTransferAdd, SosTransferPath, SosTransferPathDeal, SosTransferPathDeals, SosTransferPathMeter, SosTransferPathPoint } from '../models';
import dayjs from 'dayjs';
import { CustomFormBuilder } from '../../_shared/services/custom-form-builder.service';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../../app.config';
import { KENDO_DROPDOWNLIST } from '@progress/kendo-angular-dropdowns';

@Component({
  selector: 'app-sos-crude-deal[nomDate][pipeId][deliveryPointId][counterparties][points][addDealParams]',
  templateUrl: './sos-deal.component.html',
  styleUrls: ['./sos-deal.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  imports: [FAST_PAGE_COMMON, FAST_KENDO_COMMON, KENDO_DROPDOWNLIST, KENDO_DRAWER]
})
export class SosDealComponent implements OnInit {
  @Input() nomDate: Date
  @Input() pipeId: number
  @Input() deliveryPointId: number;
  @Input() counterparties: EntityInfo[] = []
  @Input() points: PointInfo[] = []
  @Input() addDealParams: AddDealParams = null;
  @Output() closed = new EventEmitter<boolean>()

  util = util
  icons = util.icons
  selectedDrawerText: string
  drawerItems: string[] = []
  refreshDrawerItems$ = new BehaviorSubject<string>(null)
  dealTypes: string[] = ['Baseload', 'Swing']
  supplyDealForm: util.FormModel<SosDeal>
  marketDealForm: util.FormModel<SosDeal>
  transferDealForm: util.FormModel<SosTransferAdd>

  loading$ = new BehaviorSubject<boolean>(true)
  filterCounterparties$ = new BehaviorSubject<string>(null)
  filterPoints$ = new BehaviorSubject<string>(null)
  filterPaths$ = new BehaviorSubject<string>(null)
  save$ = new Subject<SaveType>()

  counterparties$: Observable<EntityInfo[]>
  points$: Observable<PointInfo[]>
  dealDrawerItems$: Observable<DrawerItem[]>
  saveResult$: Observable<object>
  transferPathsData$: Observable<SosTransferPath[]>
  transferPaths$: Observable<SosTransferPath[]>
  transferPathDealsData$: Observable<SosTransferPathDeals>
  transferPathDeals$: Observable<SosTransferPathDeal[]>
  transferReceiptPoints$: Observable<SosTransferPathPoint[]>
  transferReceiptMeters$: Observable<SosTransferPathMeter[]>

  constructor(private messageService: MessageService, private titleService: Title, private service: SosDealService, private fb: CustomFormBuilder, private ref: ChangeDetectorRef, private dialogService: DialogService, private notify: NotifyService) {

  }

  ngOnInit(): void {
    this.supplyDealForm = this.getSupplyDealForm();
    this.marketDealForm = this.getMarketDealForm();
    this.transferDealForm = this.getTransferDealForm();

    this.dealDrawerItems$ = this.refreshDrawerItems$.pipe(
      map(() => {
        const dealDrawerItems: Array<DrawerItem> = [];
        this.drawerItems = ['Supply Deal', 'Market Deal', 'Transfer Deal'];

        this.drawerItems.forEach(str => {
          const item: DrawerItem = { text: str, icon: 'data' }
          if (this.addDealParams?.selectedDrawerText === str) {
            item.selected = true;
            this.selectedDrawerText = str;
          }
          dealDrawerItems.push(item);
        });

        if (!this.addDealParams) {
          dealDrawerItems[0].selected = true;
          this.selectedDrawerText = dealDrawerItems[0].text;
        }

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

    this.transferPathsData$ = this.service.getTransferPaths(this.pipeId).pipe(
      map(paths => {
        //filter paths by addDealParams transferSourceMeterId and transferSourceCounterpzartyId
        paths = paths.filter(path => {
          const hasNoParams = !this.addDealParams?.transferSourceMeterId && !this.addDealParams?.transferSourceCounterpartyId;
          const matchesMeter = (path.sourceMeterId === this.addDealParams?.transferSourceMeterId || path.sourceMeterId === null);
          const matchesCounterparty = (path.sourceCounterpartyId === this.addDealParams?.transferSourceCounterpartyId || path.sourceCounterpartyId === null);
          return hasNoParams || (matchesMeter && matchesCounterparty);
        });
        return paths;
      }),
      tap(paths => {
        this.loading$.next(false);
        if (paths?.length === 1)
          this.transferDealForm.patchValue({ pathId: paths[0].pathId }, { emitEvent: true });
        else
          this.focusInputTarget();
      }),
      shareReplay(1),
      catchError(err => {
        this.loading$.next(false);
        return util.handleError(err, this.messageService)
      }), retry(10)
    )

    this.saveResult$ = this.save$.pipe(
      switchMap(saveType => {
        this.loading$.next(true);
        this.supplyDealForm.disable();
        this.marketDealForm.disable();
        this.transferDealForm.disable();
        if (saveType === SaveType.supply) {
          const saveItem = this.supplyDealForm.getRawValue() as SosDeal;
          return this.service.saveSuplyDeal(saveItem);
        }
        else if (saveType === SaveType.market) {
          const saveItem = this.marketDealForm.getRawValue() as SosDeal;
          return this.service.saveMarketDeal(saveItem);
        }
        else if (saveType === SaveType.transfer) {
          const saveItem = this.transferDealForm.getRawValue() as SosTransferAdd;
          return this.service.saveTransferDeal(saveItem);
        }

      }),
      tap(() => {
        this.closed.emit(true);
        this.loading$.next(false);
      }),
      shareReplay(1),
      catchError(err => {
        this.loading$.next(false);
        this.supplyDealForm.enable();
        this.marketDealForm.enable();
        this.transferDealForm.enable();
        return util.handleError(err, this.messageService)
      }), retry(10)
    );

    this.transferPathDealsData$ = combineLatest([this.transferDealForm.get('pathId').valueChanges, this.transferPathsData$]).pipe(
      filter(() => {
        return !this.loading$.value && this.transferDealForm.get('pathId').valid;
      }),
      tap(() => {
        this.loading$.next(true);
        this.ref.detectChanges(); //needed to fix an odd issue where disabling the form clears the Path combobox immediately after selection. Perhaps this is a bug in the kendo combobox.
        this.transferDealForm.disable();
      }),
      switchMap(([pathId, transferPathsData]) => {
        const sourceMeterId = transferPathsData.find(p => p.pathId == pathId).sourceMeterId;
        return this.service.getTransferDeals(pathId, sourceMeterId, this.nomDate);
      }),
      tap(() => {
        this.transferDealForm.enable();
        this.loading$.next(false);
        this.focusInputTarget();
      }),
      shareReplay(1),
      catchError(err => {
        this.transferDealForm.enable();
        this.loading$.next(false);
        return util.handleError(err, this.messageService)
      }), retry(10)
    );

    this.counterparties$ = this.filterCounterparties$.pipe(util.filterSpecials(this.loading$, of(this.counterparties), null, 'shortName'));
    const pointsForPipe = this.points.filter(p => p.pipeId == this.pipeId);
    this.points$ = this.filterPoints$.pipe(util.filterSpecials(this.loading$, of(pointsForPipe), null, 'pointName'));
    this.transferPaths$ = this.filterPaths$.pipe(util.filterSpecials(this.loading$, this.transferPathsData$, null, 'pathName'));

    this.transferPathDeals$ = this.transferPathDealsData$.pipe(
      map(data => data.pathDeals),
      tap(data => {
        if (data?.length > 0) {
          //get the first matchingSupplyDealId where the dealNum matches the addDealParams sourceTicket
          const matchingSupplyDealId = data.find(d => d.dealNum === this.addDealParams?.sourceTicket)?.dealId;
          //if there is no match then just grab the first dealId
          const supplyDealId = matchingSupplyDealId || data[0].dealId;
          this.transferDealForm.patchValue({ supplyDealId: supplyDealId }, { emitEvent: true })
        }
        else
          this.transferDealForm.patchValue({ supplyDealId: null }, { emitEvent: true })
      })
    );

    this.transferReceiptPoints$ = combineLatest([this.transferDealForm.get('supplyDealId').valueChanges, this.transferPathDealsData$]).pipe(
      filter(() => {
        return this.transferDealForm.get('supplyDealId').valid;
      }),
      map(([supplyDealId, transferPathDealsData]) => {
        const points = transferPathDealsData.pathPoints?.filter(p => p.pointDealIds?.includes(supplyDealId));
        return points;
      }),
      tap(points => {
        if (points?.length > 0) {
          const receiptPointId = points[0].pointId;
          this.transferDealForm.patchValue({ receiptPointId: receiptPointId }, { emitEvent: true })
        }
        else
          this.transferDealForm.patchValue({ receiptPointId: null }, { emitEvent: true })
      }),
      shareReplay(1),
      catchError(err => {
        return util.handleError(err, this.messageService)
      }), retry(10)
    );

    this.transferReceiptMeters$ = combineLatest([this.transferDealForm.get('receiptPointId').valueChanges, this.transferPathDealsData$]).pipe(
      filter(() => {
        return this.transferDealForm.get('receiptPointId').valid;
      }),
      map(([receiptPointId, transferPathDealsData]) => {
        const meterIds = transferPathDealsData.pathPoints?.find(p => p.pointId == receiptPointId)?.pointMeterIds;
        const meters = transferPathDealsData.pathMeters?.filter(m => meterIds?.includes(m.meterId));
        return meters;
      }),
      tap(meters => {
        if (meters?.length > 0) {
          const receiptMeterId = meters[0].meterId;
          this.transferDealForm.patchValue({ receiptMeterId: receiptMeterId }, { emitEvent: true })
        }
        else
          this.transferDealForm.patchValue({ receiptMeterId: null }, { emitEvent: true })
      }),
      shareReplay(1),
      catchError(err => {
        return util.handleError(err, this.messageService)
      }), retry(10)
    );
  }

  save() {
    if (this.selectedDrawerText === 'Supply Deal') {
      this.supplyDealForm.markAllAsTouched();
      if (this.supplyDealForm.valid)
        this.save$.next(SaveType.supply);
      else
        this.notify.error("validation failed");
    }
    else if (this.selectedDrawerText === 'Market Deal') {
      this.marketDealForm.markAllAsTouched();
      if (this.marketDealForm.valid)
        this.save$.next(SaveType.market);
      else
        this.notify.error("validation failed");
    }
    else if (this.selectedDrawerText === 'Transfer Deal') {
      this.transferDealForm.markAllAsTouched();
      if (this.transferDealForm.valid)
        this.save$.next(SaveType.transfer);
      else
        this.notify.error("validation failed");
    }
  }

  close() {
    this.closed.emit(false);
  }

  getSupplyDealForm() {
    const fb = this.fb;
    const fg: util.FormModel<SosDeal> = this.fb.group({
      counterpartyId: fb.ctr(this.addDealParams ? this.addDealParams.supplyCounterpartyId : null, Validators.required),
      pointId: fb.ctr(this.addDealParams ? this.addDealParams.receiptPointId : null, Validators.required),
      startDate: fb.ctr(this.nomDate, Validators.required),
      endDate: fb.ctr(this.nomDate, Validators.required),
      dealType: fb.ctr(this.addDealParams ? this.addDealParams.supplyDealType : 'Swing', Validators.required),
      volume: fb.ctr(null, Validators.required)
    });

    return fg;
  }

  getMarketDealForm() {
    const fb = this.fb;
    const fg: util.FormModel<SosDeal> = this.fb.group({
      counterpartyId: fb.ctr(this.addDealParams ? this.addDealParams.marketCounterpartyId : null, Validators.required),
      pointId: fb.ctr(this.deliveryPointId, Validators.required),
      startDate: fb.ctr(this.nomDate, Validators.required),
      endDate: fb.ctr(this.nomDate, Validators.required),
      dealType: fb.ctr('Swing', Validators.required),
      volume: fb.ctr(null, Validators.required)
    });

    return fg;
  }

  getTransferDealForm() {
    const fb = this.fb;
    const fg: util.FormModel<SosTransferAdd> = this.fb.group({
      pathId: fb.ctr(null, Validators.required),
      supplyDealId: fb.ctr(null, Validators.required),
      receiptPointId: fb.ctr(null, Validators.required),
      receiptMeterId: fb.ctr(null, Validators.required),
      startDate: fb.ctr(this.nomDate, Validators.required),
      endDate: fb.ctr(dayjs(this.nomDate).endOf('month').toDate(), Validators.required),
      sourceVolume: fb.ctr(null, Validators.required)
    });

    return fg;
  }

  isDrawerItemSelected(drawerItemStr: string): boolean {
    let isSelected = false;
    if (drawerItemStr === this.selectedDrawerText)
      isSelected = true;

    return isSelected
  }

  onDrawerSelection($event: DrawerSelectEvent) {
    this.selectedDrawerText = $event.item.text;
    this.focusInputTarget();
  }

  focusInputTarget() {
    if (this.selectedDrawerText == 'Supply Deal')
      util.focusInputByClassName('supplyFocusTarget', true);
    else if (this.selectedDrawerText == 'Market Deal')
      util.focusInputByClassName('marketFocusTarget', true);
    else if (this.selectedDrawerText == 'Transfer Deal')
      util.focusInputByClassName('transferFocusTarget', true);
  }
}

