import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { LuogoRes } from '@api/models';
import { RxState } from '@rx-angular/state';
import {
  AutoCompleteCompleteEvent,
  AutoCompleteModule,
  AutoCompleteSelectEvent,
} from 'primeng/autocomplete';
import { Observable, Subject, takeUntil } from 'rxjs';

interface PlaceAutocompleteState {
  codiceLuogo: LuogoRes | null;
  autocompleteRes: AutocompleteRes;
  predictions: Prediction[];
  indirizzo: string | null;
  disabled: boolean;
}
type State = PlaceAutocompleteState;

interface Prediction {
  placeId: string;
  description: string;
}

export interface AutocompleteRes {
  indirizzo: string | null;
  cap: string | null;
  civico: string | null;
  valutazione: number | null;
  latitudine: string | null;
  longitudine: string | null;
}

@Component({
  selector: 'app-google-place-autocomplete',
  imports: [CommonModule, FormsModule, AutoCompleteModule],
  templateUrl: './google-place-autocomplete.component.html',
  styleUrl: './google-place-autocomplete.component.scss',
  providers: [RxState],
})
export class GooglePlaceAutocompleteComponent {
  model$: Observable<PlaceAutocompleteState>;
  private destroy$ = new Subject<void>();

  @Input() value!: Observable<string | null>;
  @Input() set codiceLuogo(codiceLuogo: LuogoRes | null) {
    this.state.set({ codiceLuogo: codiceLuogo ?? null });
  }
  @Input() set disabled(disabled: boolean) {
    this.state.set({ disabled: disabled ?? false });
  }
  @Output() autoCompleteSelected: EventEmitter<AutocompleteRes> =
    new EventEmitter();

  private autocompleteService = new google.maps.places.AutocompleteService();
  private request: google.maps.places.AutocompletionRequest = {
    componentRestrictions: { country: 'it' },
    input: '',
  };

  constructor(private state: RxState<State>) {
    this.model$ = this.state.select();
    this.state.set({
      predictions: [],
      indirizzo: null,
    });
  }

  ngOnInit() {
    this.value
      .pipe(takeUntil(this.destroy$))
      .subscribe((val: string | null) => {
        this.state.set({ indirizzo: val });
      });
  }

  getPlacePredictions(event: AutoCompleteCompleteEvent) {
    const value = event.query;
    const codiceLuogo = this.state.get('codiceLuogo');
    const citta = codiceLuogo?.comune;
    const provincia = codiceLuogo?.siglaProvincia;
    const stato = codiceLuogo?.stato;

    if (!value) return;
    this.request.input = `${value}${codiceLuogo ? ', ' : ''} ${citta ?? ''}${provincia ? ', ' + provincia : ''}${stato ? ', ' + stato : ''}`;
    void this.autocompleteService.getPlacePredictions(
      this.request,
      (predictions, status) => {
        if (
          status != google.maps.places.PlacesServiceStatus.OK ||
          !predictions
        ) {
          predictions = [];
        }
        this.state.set({
          predictions: predictions.map((x) => ({
            placeId: x.place_id,
            description: x.description,
          })),
        });
      },
    );
  }

  getPlaceDetail(event: AutoCompleteSelectEvent) {
    const request: google.maps.places.PlaceDetailsRequest = {
      placeId: event.value.placeId,
      fields: ['name', 'geometry', 'address_components', 'place_id', 'rating'],
    };
    const service = new google.maps.places.PlacesService(
      document.createElement('div'),
    );
    service.getDetails(request, (place, status) => {
      if (status === google.maps.places.PlacesServiceStatus.OK && place) {
        this.fillInAddress(place);
      }
    });
  }

  fillInAddress(place: google.maps.places.PlaceResult) {
    const latitudine = place.geometry?.location?.lat()?.toString() ?? null;
    const longitudine = place.geometry?.location?.lng()?.toString() ?? null;
    const valutazione = Math.round(place.rating ?? 0);

    const { indirizzo, cap, civico } = (place.address_components ?? []).reduce(
      (acc, component) => {
        switch (component.types[0]) {
          case 'street_number':
            acc.civico = component.long_name;
            break;
          case 'route':
            acc.indirizzo = component.long_name;
            break;
          case 'postal_code':
            acc.cap = component.long_name;
            break;
          case 'postal_code_suffix':
            acc.cap = acc.cap
              ? `${acc.cap}-${component.long_name}`
              : component.long_name;
            break;
        }
        return acc;
      },
      {} as { indirizzo?: string; cap?: string; civico?: string },
    );

    this.autoCompleteSelected.emit({
      indirizzo: indirizzo ?? null,
      cap: cap ?? null,
      civico: civico ?? null,
      valutazione,
      latitudine,
      longitudine,
    });
    this.state.set({ indirizzo: indirizzo ?? null });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
