import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ViewEncapsulation, OnInit, input, output, Output, inject } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { MessageService } from '../_shared/services/message.service';
import * as util from '../_shared/utils/util';
import { BehaviorSubject, Observable, Subject, catchError, combineLatest, filter, map, of, retry, shareReplay, switchMap, tap } from 'rxjs';
import { CustomFormBuilder } from '../_shared/services/custom-form-builder.service';
import { DialogService } from '@progress/kendo-angular-dialog';
import { NotifyService } from '../_shared/services/notify.service';
import { Validators } from '@angular/forms';
import { CommonDetail, countyItem, MeterCommonService, RequiredData } from './metercommon.service';
import { Router } from '@angular/router';
import { FAST_KENDO_COMMON, FAST_PAGE_COMMON } from '../app.config';

@Component({
  selector: 'app-metercommon',
  templateUrl: './metercommon.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [FAST_PAGE_COMMON, FAST_KENDO_COMMON]
})
export class MeterCommonComponent implements OnInit {
  meterId = input<number>(1);
  loading = output<boolean>();

  private service = inject(MeterCommonService);
  private messageService = inject(MessageService);
  private titleService = inject(Title);
  private fb = inject(CustomFormBuilder);
  private ref = inject(ChangeDetectorRef);
  private dialogService = inject(DialogService);
  private notify = inject(NotifyService);
  private router = inject(Router);

  constructor() {
    this.detailInitialValues = this.commonDetailForm.getRawValue() as CommonDetail;
  }

  ngOnInit() {
    this.refreshRequiredData$.next(util.RefreshType.SelfOnly);
  }

  util = util;

  commonDetailForm = this.getCommonDetailForm();
  localRequiredData: RequiredData;
  hasModifyPermission = false;
  counties: countyItem[];
  detailInitialValues: CommonDetail;
  mySelection: number[] = [];
  detailOpened$ = new BehaviorSubject<boolean>(false)
  refreshRequiredData$ = new BehaviorSubject(null)
  commonDetailLoading$ = new BehaviorSubject<boolean>(true)
  refreshDetail$ = new BehaviorSubject<number>(null)
  refreshCountyForState$ = new BehaviorSubject(null)
  disableMap$ = new BehaviorSubject<boolean>(true)
  save$ = new Subject<util.SaveType>()
  delete$ = new Subject()

  getCommonDetailForm() {
    const fb = this.fb;
    const fg: util.FormModel<CommonDetail> = fb.group({
      meterId: fb.ctr(0, Validators.required),
      name: fb.ctr(null, Validators.required),
      inactiveDate: fb.ctr(null),
      stateId: fb.ctr(null),
      countyId: fb.ctr(null),
      locationId: fb.ctr(null),
      leaseId: fb.ctr(null),
      latitude: fb.ctr(null),
      longitude: fb.ctr(null),
      producerIds: fb.ctr(null),
    });

    return fg;
  }

  isFormValid(): boolean {
    if (this.commonDetailForm.valid) {
      return true;
    }
    else {
      return false;
    }
  }

  isFormDirty(): boolean {
    if (this.commonDetailForm.dirty) {
      return true;
    }
    else {
      return false;
    }
  }

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

  //this updates localDetailLoading whenever detailLoading$ changes so that we can check the loading status in this class
  localDetailLoading = true;
  detailLoadingWatcher$ = this.commonDetailLoading$.pipe(
    tap(isLoading => {
      this.loading.emit(isLoading);
    }),
    shareReplay(1)
  )

  @Output() outputLoading$: Observable<boolean> = this.detailLoadingWatcher$;

  detail$ = this.refreshDetail$.pipe(
    filter(id => id !== null),
    switchMap(id => {
      this.commonDetailLoading$.next(true);
      this.commonDetailForm.disable();
      this.commonDetailForm.reset();
      if (id === 0)
        return of(this.detailInitialValues);
      else
        return this.service.getDetail(id);
    }),
    map(result => {
      const detail: CommonDetail = result;

      if (detail) {
        util.convertToDates(detail);
        this.commonDetailForm.setValue(detail);
      }
      return detail;
    }),
    tap(() => {
      this.detailFinishedLoading();
      this.refreshCountyForState$.next(null);
      this.coordinateChanged(this);
    }),
    shareReplay(1),
    catchError(err => {
      return util.handleError(err, this.messageService)
    }), retry(10)
  )

  detailFinishedLoading(): void {
    this.commonDetailForm.enable();
    this.commonDetailLoading$.next(false);
    util.focusInputTarget();
  }

  getCommonDetail(): CommonDetail {
    const detail: CommonDetail = this.commonDetailForm.getRawValue();
    return detail;
  }

  coordinateChanged(instance: MeterCommonComponent): void {
    const lat = instance.commonDetailForm.get('latitude').value;
    const long = instance.commonDetailForm.get('longitude').value;
    if (lat !== null && lat !== 0 && long !== null && long !== 0) {
      this.disableMap$.next(false);
    }
    else {
      this.disableMap$.next(true);
    }
  }

  getMapUrl(): string {
    let url = null;
    const latitude = this.commonDetailForm.value.latitude
    const longitude = this.commonDetailForm.value.longitude
    if (latitude && longitude) {
      url = "https://www.google.com/maps/search/?api=1&query={lat},{long}";
      url = url.replace(/{lat}/g, latitude.toFixed(6));
      url = url.replace(/{long}/g, longitude.toFixed(6));
    }
    return url;
  }

  externalNavigate(path: string) {
    this.router.navigate(['/externalRedirect', { externalUrl: path }], {
      skipLocationChange: false,
    });
  }

  subCountyForState$ = this.refreshCountyForState$.pipe(
    map(() => {
      const selectedStateId = this.commonDetailForm.get('stateId').value ?? null;
      if (selectedStateId === null) {
        this.commonDetailForm.patchValue({ countyId: null }, { emitEvent: false });
        return
      }
      else {
        const filteredCounties = this.counties.filter(x => x.stateId === selectedStateId);
        const countyId = this.commonDetailForm.get('countyId').getRawValue();
        if (filteredCounties && filteredCounties.findIndex(x => x.countyId === countyId) === -1)
          this.commonDetailForm.patchValue({ countyId: null }, { emitEvent: false });

        if (filteredCounties.length === 1)
          this.commonDetailForm.patchValue({ countyId: filteredCounties[0].countyId }, { emitEvent: false });

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

  stateChanged$ = this.commonDetailForm.get('stateId').valueChanges.pipe(
    filter(() => {
      return !this.commonDetailLoading$.value;
    }),
    tap(() => {
      setTimeout(() => { //waits for form controls to update before calling refresh
        this.refreshCountyForState$.next(null);
      });
    }),
    shareReplay(1)
  )

  filterStates$ = new BehaviorSubject<string>(null)
  states$ = this.filterStates$.pipe(util.filterIdNames(this.commonDetailLoading$, this.requiredData$, 'stateNames'));

  filterCounty$ = new BehaviorSubject<string>(null)
  county$ = this.filterCounty$.pipe(util.filterSpecials(this.commonDetailLoading$, this.subCountyForState$, null, 'countyName'));

  filterProducers$ = new BehaviorSubject<string>(null)
  producers$ = this.filterProducers$.pipe(util.filterIdNames(this.commonDetailLoading$, this.requiredData$, 'producers'));

  filterLeases$ = new BehaviorSubject<string>(null)
  leases$ = this.filterLeases$.pipe(util.filterIdNames(this.commonDetailLoading$, this.requiredData$, 'blocks'));

  filterLocations$ = new BehaviorSubject<string>(null)
  locations$ = this.filterLocations$.pipe(util.filterIdNames(this.commonDetailLoading$, this.requiredData$, 'locations'));

}
