import { inject, Injectable, NgZone } from '@angular/core';

import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Store } from '@ngxs/store';

import { GetZoneAction, SaveLocationAction } from '../action/zone.action';

@Injectable({
  providedIn: 'root',
})
export class LocationService {
  private store = inject(Store);
  private ngZone = inject(NgZone);
  private model = inject(NgbModal);

  public googleMapApiKey: string | undefined;
  public is_loading: boolean = false;
  public is_zone_selected: boolean = true;
  public zone_toggle: boolean = false;
  public is_zone: boolean;
  public term: string;
  public is_valid_api_key: boolean;

  loadGoogleMapApi(): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      if (window.google && window.google.maps) {
        resolve(); // Google Maps already loaded
        return;
      }

      if (!document.getElementById('google-maps-script')) {
        const script = document.createElement('script');
        script.id = 'google-maps-script';
        script.src = `https://maps.googleapis.com/maps/api/js?key=${'YOUR_GOOGLE_API_KEY'}&libraries=places`;
        script.async = true;
        script.defer = true;

        script.onload = () => {
          console.warn('Google Maps API loaded successfully.');
          resolve();
        };

        script.onerror = () => {
          console.error('Failed to load Google Maps API.');
          reject();
        };

        document.body.appendChild(script);
      }
    });
  }

  getCurrentLocation(isModel: boolean = false): void {
    void this.loadGoogleMapsAPI().then(isValid => {
      this.is_valid_api_key = isValid;
      if (!isValid) {
        alert('API Key is invalid or expired! Using only latitude & longitude.');
      }
      this.fetchUserLocation(isValid, isModel);
    });
  }

  loadGoogleMapsAPI(): Promise<boolean> {
    return new Promise(resolve => {
      if (window.google && window.google.maps) {
        void this.verifyApiKey().then(resolve);
      } else {
        resolve(false);
      }
    });
  }

  verifyApiKey(): Promise<boolean> {
    return new Promise(resolve => {
      const testUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=India&key={'YOUR_GOOGLE_API_KEY'}`;

      fetch(testUrl)
        .then(response => response.json())
        .then(data => {
          if (data.status === 'REQUEST_DENIED' || data.status === 'INVALID_REQUEST') {
            resolve(false);
          } else {
            resolve(true);
          }
        })
        .catch(() => resolve(false));
    });
  }

  fetchUserLocation(useGeocoding: boolean, isModel: boolean): void {
    if (!navigator.geolocation) {
      alert('Geolocation is not supported by your browser.');
      return;
    }

    this.ngZone.run(() => (this.is_loading = true));

    navigator.geolocation.getCurrentPosition(
      position => {
        const lat = position.coords.latitude;
        const lng = position.coords.longitude;

        if (useGeocoding) {
          this.reverseGeocode(lat, lng, isModel);
        } else {
          this.processLocation(lat, lng, null, isModel);
        }
      },
      error => {
        console.error('Error getting current location:', error);
        alert('Unable to fetch your current location. Please try again.');
        this.ngZone.run(() => (this.is_loading = false));
      },
    );
  }

  private reverseGeocode(lat: number, lng: number, isModel: boolean): void {
    const geocoder = new window.google.maps.Geocoder();
    void geocoder.geocode({ location: { lat, lng } }, (results, status) => {
      if (status === 'OK' && results?.[0]) {
        this.processLocation(lat, lng, results[0].formatted_address, isModel);
      } else {
        alert('Failed to fetch location details, using coordinates instead.');
        this.processLocation(lat, lng, null, isModel);
      }
    });
  }

  private processLocation(
    lat: number,
    lng: number,
    address: string | null,
    isModel: boolean,
  ): void {
    this.ngZone.run(() => {
      const locationData = address ? address : `Lat: ${lat}, Lng: ${lng}`;

      if (isModel) {
        this.term = locationData;
      }

      this.store.dispatch(new SaveLocationAction(locationData));

      if (isModel) {
        this.model.dismissAll();
      }

      this.is_loading = false;
      this.zone_toggle = false;
      this.is_zone_selected = false;

      this.store.dispatch(new GetZoneAction({ lat, lng }));
    });
  }
}
