import { CommonModule } from '@angular/common';
import { Component, ViewChild } from '@angular/core';
import {
  FormControl,
  FormGroup,
  FormsModule,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';
import {
  GoogleMap,
  GoogleMapsModule,
  MapInfoWindow,
  MapMarker,
} from '@angular/google-maps';
import { LuogoRes, StrutturePubblicheResDto, TipoStruttura } from '@api/models';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import {
  faCity,
  faGlobeEurope,
  faHospital,
  faMapLocationDot,
} from '@fortawesome/free-solid-svg-icons';
import { RxState } from '@rx-angular/state';
import { MessageService, ToastMessageOptions } from 'primeng/api';
import { AutoCompleteModule } from 'primeng/autocomplete';
import { ButtonModule } from 'primeng/button';
import { DividerModule } from 'primeng/divider';
import { DropdownModule } from 'primeng/dropdown';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
import { InputTextModule } from 'primeng/inputtext';
import { MultiSelectModule } from 'primeng/multiselect';
import { RatingModule } from 'primeng/rating';
import { Table, TableModule } from 'primeng/table';
import { ToastModule } from 'primeng/toast';
import { ToggleButtonModule } from 'primeng/togglebutton';
import {
  catchError,
  EMPTY,
  endWith,
  map,
  Observable,
  of,
  startWith,
  switchMap,
  tap,
} from 'rxjs';
import { StrutturePubblicheControllerFindManyStrutture$Params } from '../../../../api/fn/strutture-pubbliche/strutture-pubbliche-controller-find-many-strutture';
import { TableColumn } from '../../shared';
import { StrutturePubblicheService } from './strutture-pubbliche.service';

const cercaStruttureFormGroupFunc = () => {
  return new FormGroup({
    tipologiaStruttura: new FormControl<string | null>(null),
    luogo: new FormControl<LuogoRes | null>(null, {
      validators: [Validators.required],
    }),
    indirizzo: new FormControl<string | null>(null),
  });
};
export type cercaStruttureForm = ReturnType<typeof cercaStruttureFormGroupFunc>;
export type cercaStruttureFormValue = cercaStruttureForm['value'];

export interface CercaStruttureComponentState {
  strutture: StrutturePubblicheResDto[];
  tipologieStrutture: TipoStruttura[];
  filteredLuoghiEsteso: Array<LuogoRes & { dataKey: string }>;

  cercaStruttureFormSubmitted: boolean;
  tipologieStruttureAreLoading: boolean;
  struttureAreLoading: boolean;
  luoghiAreLoading: boolean;
  isSearching: boolean;
}
type State = CercaStruttureComponentState;

@Component({
  selector: 'app-strutture-pubbliche',
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    ButtonModule,
    DividerModule,
    MultiSelectModule,
    ToggleButtonModule,
    GoogleMapsModule,
    AutoCompleteModule,
    FontAwesomeModule,
    InputTextModule,
    DropdownModule,
    ToastModule,
    TableModule,
    IconFieldModule,
    InputIconModule,
    RatingModule,
  ],
  providers: [RxState],
  templateUrl: './strutture-pubbliche.component.html',
  styleUrl: './strutture-pubbliche.component.scss',
})
export class StrutturePubblicheComponent {
  model$: Observable<CercaStruttureComponentState>;
  messages: ToastMessageOptions[] = [];

  @ViewChild('gmap', { static: false }) map!: GoogleMap;
  @ViewChild(MapInfoWindow) infoWindow!: MapInfoWindow;
  options: google.maps.MapOptions;
  markerOptions: google.maps.MarkerOptions[] = [];
  indirizzoGmaps: string = 'indirizoooo';

  struttureTable!: Table;
  calcolaIndirizzoDa: string | null | undefined;
  @ViewChild('struttureTable') set table(table: Table) {
    if (!table || this.struttureTable === table) return;
    this.struttureTable = table;
  }

  cercaStruttureForm = cercaStruttureFormGroupFunc();
  faCity = faCity;
  faGlobeEurope = faGlobeEurope;
  faMapLocationDot = faMapLocationDot;
  faHospital = faHospital;
  struttureConCoords: StrutturePubblicheResDto[] = [];

  modelLuogo!: LuogoRes | null;

  cols: TableColumn[] = [
    {
      header: 'Strutture',
      field: 'struttura.nome',
      sortField: 'struttura.nome',
    },
    {
      header: 'Indirizzo',
      field: 'indirizzo',
    },

    {
      header: 'Posizione',
      field: 'posizione',
    },
  ];

  constructor(
    private strutturePubblicheService: StrutturePubblicheService,
    public state: RxState<State>,
    private messageService: MessageService,
  ) {
    this.model$ = this.state.select();
    this.state.set({
      strutture: undefined,
      tipologieStrutture: [],
      filteredLuoghiEsteso: [],

      cercaStruttureFormSubmitted: false,
      tipologieStruttureAreLoading: false,
      struttureAreLoading: false,
      luoghiAreLoading: false,
      isSearching: false,
    });

    const fetchTipologieStrutture$ = of(EMPTY).pipe(
      switchMap(() =>
        this.strutturePubblicheService.getTipologieStrutture().pipe(
          map((res) => ({
            tipologieStrutture: res,
          })),
          catchError(() => of({ tipologieStrutture: [] })),
          startWith({ tipologieStruttureAreLoading: true }),
          endWith({ tipologieStruttureAreLoading: false }),
        ),
      ),
    );
    this.state.connect(fetchTipologieStrutture$);

    this.options = {
      center: { lat: 45.4627123, lng: 9.1075213 },
      zoom: 8,
      // mapId: 'c18657fa9d1be788abd38482',
    };
  }

  fetchLuogo(
    event: { originalEvent: Event; query: string },
    flagAttivo?: number,
  ) {
    if (!event.query || event.query.length < 3) {
      this.state.set({ filteredLuoghiEsteso: [] });
      return;
    }
    const fetchCities$ = this.strutturePubblicheService
      .getLuoghiEsteso(event.query, flagAttivo)
      .pipe(
        catchError((err) => {
          return of(null);
        }),
        map((res) => ({
          filteredLuoghiEsteso:
            res?.rows.map((res) => ({
              ...res,
              dataKey: res.tipo + ' → ' + res.luogo,
            })) ?? [],
        })),
      );
    this.state.connect(fetchCities$);
  }

  getStrutture() {
    this.state.set({
      cercaStruttureFormSubmitted: true,
    });
    if (this.cercaStruttureForm.invalid) {
      return;
    }

    const _form = this.cercaStruttureForm.value;

    this.cercaStruttureForm.disable();
    const params: StrutturePubblicheControllerFindManyStrutture$Params = {
      indirizzo: _form.indirizzo,
      tipoStruttura: _form.tipologiaStruttura,
      luogo: _form.luogo!.luogo!,
      'luogo.tipo': _form.luogo!.tipo!,
    };
    this.calcolaIndirizzoDa = _form.indirizzo;

    const strutture$ = this.strutturePubblicheService.getStrutture(params).pipe(
      catchError((err) => {
        this.messageService.add({
          severity: 'error',
          life: 5000,
          summary: 'Attenzione!',
          detail:
            err.status === 400
              ? 'Il luogo è obbligatorio'
              : 'Impossibile recuperare la lista delle strutture in questo momento, riprova più tardi',
        });
        return of([] as StrutturePubblicheResDto[]);
      }),
      tap((res) => {
        this.cercaStruttureForm.enable();
        if (!res) return;
        this.gMapMarkers(res, false);
      }),
      map((res) => ({ strutture: res })),
      startWith({ struttureAreLoading: true, isSearching: true }),
      endWith({ struttureAreLoading: false, isSearching: false }),
    );

    this.state.connect(strutture$);
  }

  openInfoWindow(marker: MapMarker, index: number) {
    if (this.struttureConCoords.length === index) {
      // se è home icon (il push è stato fatto alla fine, quindi sarà l'ultimo nella lista)
      const contentStringHome = `<div>
          <h1 style="margin-top: 0px; margin-bottom: 0px; font-size:16px;">${this.indirizzoGmaps}</h1>
        </div>`;
      this.infoWindow.infoWindow?.setContent(contentStringHome);
      this.infoWindow.open(marker);
    } else {
      const info = this.struttureConCoords[index];
      const contentString = `
              <div>
                <div>
                  <span class="color-primary" style="font-size: 20px;font-weight: bold; color: var(--ion-color-primary);">${
                    info.struttura.stelline
                  }</span>
                  <h1 style="margin-top: 0px; margin-bottom: 0px; font-size:16px; color: #222428;">${
                    info.struttura.nome
                  }</h1>
                </div>
                <div>
                  <p class="m-t-0 m-b-0" style="color: #222428">
                    ${info.struttura.indirizzo}${
                      info.struttura.numeroCivico
                        ? ', ' + info.struttura.numeroCivico
                        : ''
                    }
                  </p>
                  <p class="m-t-0 m-b-0" style="color: #222428;">
                  ${info.struttura.cap} ${info.struttura.codiceLuogo.comune} ${
                    info.struttura.codiceLuogo.provincia
                      ? '(' + info.struttura.codiceLuogo.siglaProvincia + ')'
                      : ''
                  }
                  </p>
                </div>
                <div class="mt-2">
                  <a class="hover:underline text-primary-700" target="_blank" href="${this.calculateMapsLink(info, false)}"><i class="pi pi-map-marker"></i>Apri su Google Maps</a>
                 </div>
              </div>
              `;
      this.infoWindow.infoWindow?.setContent(contentString);
      this.infoWindow.open(marker);
      this.infoWindow.infoWindow?.addListener('domready', () => {
        const el = document.getElementById(`facility_${info?.struttura.id}`);
        if (el) {
          el.addEventListener('click', () => {
            // this.next(info);
          });
        }
      });
    }
  }

  gMapMarkers(items: StrutturePubblicheResDto[], filter: boolean) {
    // Filtra solo strutture con coordinate valide
    const itemsWithCoords = items.filter((x) => {
      const lat = this.parseCoord(x.struttura.latitudine);
      const lng = this.parseCoord(x.struttura.longitudine);
      return lat !== null && lng !== null;
    });
    this.struttureConCoords = itemsWithCoords;

    if (!itemsWithCoords.length) {
      this.markerOptions = [];
      return;
    }

    // Calcola centro mappa
    const firstItem = itemsWithCoords[0];
    const centerLat =
      this.parseCoord(firstItem.struttura.latitudine) ?? 45.472520078847595;
    const centerLng =
      this.parseCoord(firstItem.struttura.longitudine) ?? 9.194890732834459;

    this.options = {
      center: { lat: centerLat, lng: centerLng },
      zoom: itemsWithCoords.length ? 13 : 11,
      // mapId: 'c18657fa9d1be788abd38482',
    };

    // Marker strutture
    this.markerOptions = itemsWithCoords.map((x, i) => ({
      position: {
        lat: this.parseCoord(x.struttura.latitudine)!,
        lng: this.parseCoord(x.struttura.longitudine)!,
      },
      title: x.struttura.nome,
      draggable: false,
      icon:
        !filter && i < 6
          ? 'https://maps.google.com/mapfiles/kml/paddle/blu-stars.png'
          : undefined,
    }));

    // Marker "home"
    if (firstItem.startingLocation) {
      this.markerOptions.push({
        position: firstItem.startingLocation,
        title: this.indirizzoGmaps,
        icon: {
          url: 'https://maps.google.com/mapfiles/kml/pal3/icon56.png',
          scaledSize: new google.maps.Size(50, 50),
        },
      });
    }

    // Crea i marker cluster dopo che la mappa è pronta
    // setTimeout(() => {
    //   if (!this.map?.googleMap) return;

    //   const markers = this.markerOptions
    //     .filter((opt) => !!opt.position)
    //     .map((opt) => {
    //       return new google.maps.marker.AdvancedMarkerElement({
    //         position: opt.position!,
    //         title: opt.title,
    //         map: this.map.googleMap!,
    //       });
    //     });

    //   new MarkerClusterer({
    //     map: this.map.googleMap,
    //     markers,
    //   });
    // });
  }

  private parseCoord(value?: string | null): number | null {
    if (!value) return null;
    const n = Number(value.replace(',', '.'));
    return Number.isFinite(n) ? n : null;
  }

  externalLink(url: string) {
    window.open('//' + url, '_blank');
  }

  applyFilterGlobal($event: Event, stringVal: string) {
    console.log(this.struttureTable);

    this.struttureTable.filterGlobal(
      ($event.target as HTMLInputElement).value,
      stringVal,
    );
  }

  calculateMapsLink(struttura: StrutturePubblicheResDto, open = true): string {
    // https://www.google.com/maps/dir/?api=1&origin=45.476333943968626,9.17169124076061&destination=45.4692422,9.16562&travelmode=driving
    // https://www.google.com/maps/dir/?api=1&destination=45.4692422,9.16562&travelmode=driving

    const baseUrl = 'https://www.google.com/maps/dir/?api=1';
    const travelMode = 'driving';
    const destinationMode: 'latlng' | 'nameAddress' = 'nameAddress';

    let url = ((destinationMode: 'latlng' | 'nameAddress') => {
      switch (destinationMode) {
        case 'nameAddress': {
          let address = `${struttura.struttura.indirizzo}${
            struttura.struttura.numeroCivico
              ? ', ' + struttura.struttura.numeroCivico
              : ''
          }`;
          address += `${struttura.struttura.cap} ${struttura.struttura.codiceLuogo.comune}(${struttura.struttura.codiceLuogo.siglaProvincia})`;
          // address += `${struttura.struttura.codiceLuogo.provincia ? '(' + struttura.struttura.codiceLuogo.siglaProvincia + ')' : ''}`;

          const destination = encodeURIComponent(
            `${struttura.struttura.nome}, ${address}`,
          );
          return `${baseUrl}&destination=${destination}`;
        }
        case 'latlng':
          return `${baseUrl}&destination=${struttura.struttura.latitudine},${struttura.struttura.longitudine}`;
      }
    })(destinationMode);

    if (this.calcolaIndirizzoDa) {
      url = `${url}&origin=${this.calcolaIndirizzoDa}`;
    }
    const luogo = this.cercaStruttureForm.controls.luogo.value;
    if (luogo) {
      url = `${url}, ${luogo.luogo}`;
    }

    url = `${url}&travelmode=${travelMode}`;

    if (open) window.open(url, '_blank');
    return url;
  }

  checkLuogo() {
    //Hack: rimosso dall'autocomplete forceSelection perchè non funzionava come previsto, selezionando ad esempio "Milano" (comune), selezionava "Milano" (comune).
    const luogo = this.cercaStruttureForm.controls['luogo'].value;
    if (typeof luogo === 'string') {
      this.cercaStruttureForm.controls['luogo'].setValue(null);
    }
  }
}
