import { bindable, customAttribute, autoinject, bindingMode } from 'aurelia-framework';
import { CustomLogger } from '../../modules/custom-logger';
import { Config } from '../../config';
import { UIInternal } from '../../utils/ui-internal';
import { I18N } from 'aurelia-i18n';

@autoinject
@customAttribute('dw-place-autocomplete')
export class PlaceAutocomplete {
  private GOOGLE_API_KEY;
  private inputElement: HTMLInputElement;
  private script: HTMLScriptElement;
  private autocomplete: google.maps.places.Autocomplete;
  private originalConsoleError;

  @bindable({ defaultBindingMode: bindingMode.fromView }) route: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) longRoute: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) streetNumber: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) locality: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) postalCode: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) country: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) countrycode: string;
  @bindable({ defaultBindingMode: bindingMode.fromView }) latitude: number;
  @bindable({ defaultBindingMode: bindingMode.fromView }) longitude: number;
  @bindable({ defaultBindingMode: bindingMode.twoWay }) model: PlaceAutocompleteModel;
  @bindable({ defaultBindingMode: bindingMode.twoWay }) config: PlaceAutocompleteConfig;


  constructor(private element: Element, private logger: CustomLogger, envConfig: Config, private i18n: I18N) {
    if (envConfig?.globalConfig.googleApiKey == null && logger.LogError != null) {
      logger.LogError('Google API Key not found; Set config.googleApiKey !', null, 'Datacontext', false);
    }
    this.GOOGLE_API_KEY = envConfig.globalConfig.googleApiKey;
    this.originalConsoleError = console.error;
  }

  attached() {
    if (this.GOOGLE_API_KEY == null) return;

    if (this.element.tagName === 'INPUT') {
      this.inputElement = this.element as HTMLInputElement;
    } else if (this.element.tagName === 'UI-INPUT') {
      let inp = this.element.getElementsByTagName('input');
      if (inp != null && inp.length) this.inputElement = inp.item(0);
    }

    if (this.inputElement == null) {
      if (this.logger && this.logger.LogError)
        this.logger.LogError('dw-place-autocomplete must be placed on an INPUT or UI-INPUT tag element only.', null, 'Datacontext', false);
      return;
    }

    this.initConfig();
    this.injectScript();
  }

  detached() {
    if (this.script != null) {
      this.script.remove();
    }
    console.error = this.originalConsoleError;
  }

  /**
   * init the default config of the google-maps-places-autocomplete requests.
   *
   * @private
   * @memberof PlaceAutocomplete
   */
  private initConfig(): void {
    if (this.config == null) {
      this.config = {} as PlaceAutocompleteConfig;
    }
    // Default country
    if (this.config.country == null) {
      this.config.country = 'be';
    }

    // Default language
    if (this.config.language == null) {
      this.config.language = navigator.language;
    }
  }

  /**
   * Init and manage google-maps-places-autocomplete results.
   *
   * @private
   * @memberof PlaceAutocomplete
   */
  private initAutocomplete(): void {
    if (this.inputElement != null) {
      const center = { lat: 50.62627, lng: 4.69837 }; // Walhain: Belgium center

      let boxSize = 1.2; // Create a bounding box with sides ~CALC(boxSize * 100) km away from the center point
      const defaultBounds = {
        north: center.lat + boxSize,
        south: center.lat - boxSize,
        east: center.lng + boxSize,
        west: center.lng - boxSize,
      };

      this.autocomplete = new google.maps.places.Autocomplete(this.inputElement, {
        componentRestrictions: { country: this.config.country },
        fields: ["address_components", "geometry"],
        types: ["address"],
        bounds: defaultBounds
      });

      if (this.config.focus == true) {
        this.inputElement.focus();
      }

      this.autocomplete.addListener('place_changed', () => {
        let placeResult = this.autocomplete.getPlace();
        if (placeResult.address_components != null) {
          let placeComponents = placeResult.address_components;
          if (placeComponents.length) {
            this.route = placeComponents.find(c => c.types.find(t => t == 'route'))?.long_name;
            this.streetNumber = placeComponents.find(c => c.types.find(t => t == 'street_number'))?.long_name;
            this.locality = placeComponents.find(c => c.types.find(t => t == 'locality'))?.long_name
              ?? placeComponents.find(c => c.types.find(t => t == 'sublocality'))?.long_name
              ?? placeComponents.find(c => c.types.find(t => t == 'postal_town'))?.long_name;
            this.postalCode = placeComponents.find(c => c.types.find(t => t == 'postal_code'))?.long_name;
            this.country = placeComponents.find(c => c.types.find(t => t == 'country'))?.long_name;
            this.countrycode = placeComponents.find(c => c.types.find(t => t == 'country'))?.short_name;

            this.longRoute = this.route;
            if (this.streetNumber != null) {
              this.longRoute += ', ' + this.streetNumber;
            }

            if (placeResult.geometry?.location != null) {
              this.latitude = placeResult.geometry.location.lat();
              this.longitude = placeResult.geometry.location.lng();
            }

            this.completeModel();

            this.inputElement.value = null;
          }
        }
      });

      let _this = this;
      console.error = function (...args: any[]) {
        _this.originalConsoleError.apply(console, args);
        if (args.length > 0 && (args[0].toString() as string).includes("You have exceeded your daily request quota for this API")) {
          _this.logger.LogError(_this.i18n.tr("general.errorLimitExceed"), args[0], 'PlaceAutocomplete', true)
        }
      };
    }
  }

  /**
   * Complete this <AutocompleteModel>model and callback the modelChanged function.
   */
  private completeModel(): void {
    let model = {} as PlaceAutocompleteModel;
    model.route = this.route;
    model.streetNumber = this.streetNumber;
    model.longRoute = this.longRoute;
    model.postalCode = this.postalCode;
    model.locality = this.locality;
    model.country = this.country;
    model.countryCode = this.countrycode;
    model.latitude = this.latitude;
    model.longitude = this.longitude;
    this.model = model;
  }

  /**
   * Inject the script api of Google after the body.
   *
   * @memberof PlaceAutocomplete
   */
  private injectScript(): void {
    if (this.inputElement) {
      let doc: Document = this.inputElement.ownerDocument;
      this.script = doc.createElement('script');
      this.script.setAttribute('src',
        `https://maps.googleapis.com/maps/api/js?key=${this.GOOGLE_API_KEY}&libraries=places&v=weekly&language=${this.config.language}`
      );
      this.script.async = true;
      this.script.onload = (d) => {
        UIInternal.queueTask(() => {
          this.initAutocomplete();
        });
      };
      doc.body.after(this.script);
    }
  }
}



export interface PlaceAutocompleteConfig {
  country?: string | Array<string>;
  focus?: boolean;
  language?: string;
}

export interface PlaceAutocompleteModel {
  route?: string;
  streetNumber?: string;
  longRoute?: string;
  locality?: string;
  postalCode?: string;
  country?: string;
  countryCode?: string;
  latitude?: number;
  longitude?: number;
}