import {AsyncPipe} from '@angular/common';
import {
  AfterContentInit,
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  Input,
  OnDestroy,
  ViewChild,
} from '@angular/core';
import {IonFabButton} from '@ionic/angular/standalone';
import {MapService} from '@maplibre/ngx-maplibre-gl';
import {FitBoundsOptions, GeolocateControl, GeolocateControlOptions} from 'maplibre-gl';
import {Map as MapLibre} from 'maplibre-gl';
import {BehaviorSubject} from 'rxjs';
import {IonIconDirective} from 'src/app/util/ion-icon/ion-icon.directive';

type WatchState = InstanceType<typeof GeolocateControl>['_watchState'];

class CustomGeolocateControl extends GeolocateControl {
  constructor(
    public _container: HTMLElement,
    watchState: BehaviorSubject<WatchState>,
    options: GeolocateControlOptions,
  ) {
    super(options);
    Object.defineProperty(this, '_watchState', {
      get() {
        return watchState.value;
      },
      set(value: WatchState) {
        watchState.next(value);
      },
    });
  }

  override onAdd(map: MapLibre): HTMLElement {
    const container = this._container;
    this._container = document.createElement('div');
    this._map = map;
    this._setupUI(true);
    this._container = container;
    return this._container;
  }

  override onRemove() {}
}

@Component({
  selector: 'stapps-geolocate-control',
  templateUrl: './geolocate-control.html',
  styleUrl: './geolocate-control.scss',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [AsyncPipe, IonIconDirective, IonFabButton],
})
export class GeolocateControlComponent implements AfterContentInit, OnDestroy {
  @Input() position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';

  @Input() positionOptions?: PositionOptions;

  @Input() fitBoundsOptions?: FitBoundsOptions;

  @Input() trackUserLocation?: boolean;

  @Input() showUserLocation?: boolean;

  @ViewChild('content', {static: true}) content: ElementRef;

  watchState = new BehaviorSubject<WatchState>('OFF');

  control: CustomGeolocateControl;

  constructor(private mapService: MapService) {}

  ngAfterContentInit() {
    this.control = new CustomGeolocateControl(this.content.nativeElement, this.watchState, {
      positionOptions: this.positionOptions,
      fitBoundsOptions: this.fitBoundsOptions,
      trackUserLocation: this.trackUserLocation,
      showUserLocation: this.showUserLocation,
    });
    this.mapService.mapCreated$.subscribe(() => {
      this.mapService.addControl(this.control, this.position);
    });
  }

  ngOnDestroy(): void {
    this.mapService.removeControl(this.control);
  }
}
