import {
  API, BaseService, LocalStorage, kServiceOfflineWeather, kServiceWeather,
  kWidgetWeather,
} from '@bthlabs/homehub-core';
import isEqual from 'lodash/isEqual';
import noop from 'lodash/noop';
import {DateTime} from 'luxon';

import {Clock} from 'src/lib/clock';

export class WeatherService extends BaseService {
  static kind = kServiceWeather;
  static widget = kWidgetWeather;
  static emptyCharacteristics () {
    return {
      'city': '',
      'units': 'metric',
    };
  }
  async start () {
    const result = await API.Services.start(
      this.kind, this.instance, this.characteristics
    );

    this.notify(result);
  }
  async stop () {
    return API.Services.stop(this.kind, this.instance);
  }
  setCharacteristics (newCharacteristics) {
    const shouldRestart = Object.keys(
      WeatherService.emptyCharacteristics()
    ).some(
      (key) => {
        return !isEqual(this.characteristics[key], newCharacteristics[key]);
      }
    );
    super.setCharacteristics(newCharacteristics);

    if (shouldRestart) {
      this.restart();
    }
  }
}

export class OfflineWeatherService extends BaseService {
  static apiURL = 'https://api.openweathermap.org/data/2.5/weather';
  static availableOnline = false;
  static availableOffline = true;
  static kind = kServiceOfflineWeather;
  static widget = kWidgetWeather;
  static emptyCharacteristics () {
    return {
      city: '',
      units: 'metric',
      apiKey: '',
    };
  }
  constructor (spec) {
    super(spec);
    this.instanceKey = `${this.kind}_${spec.instance}`.replaceAll('-', '_');
    this.callbackKey = `${this.instanceKey}_callback`;
    this.lastScriptElement = null;
    this.lastData = null;
    this.lastRefreshDt = null;
  }
  async start () {
    window[this.callbackKey] = this.onWeatherCallback;

    const lastData = LocalStorage.getItem(`${this.instanceKey}_lastData`);
    if (lastData !== null) {
      this.lastData = lastData;
      this.lastRefreshDt = DateTime.fromSeconds(this.lastData.dt);
    }

    this.refresh();

    Clock.instance().addEventListener('tick', this.onClockTick);
  }
  async stop () {
    window[this.callbackKey] = noop;

    Clock.instance().removeEventListener('tick', this.onClockTick);
  }
  initialState () {
    return {
      error: null,
      data: this.lastData,
    };
  }
  onClockTick = () => {
    if (this.shouldRefresh()) {
      this.refresh();
    }
  }
  shouldRefresh = () => {
    if (this.lastData === null) {
      return true;
    }

    const lastUpdateDelta = Clock.instance().now.diff(this.lastRefreshDt).as('seconds');
    return (lastUpdateDelta >= 600);
  }
  onWeatherCallback = (data) => {
    document.body.removeChild(this.lastScriptElement);

    LocalStorage.setItem(`${this.instanceKey}_lastData`, data);
    this.lastData = data;
    this.lastRefreshDt = Clock.instance().now;

    this.notify({
      error: null,
      data: this.lastData,
    });
  }
  refresh = () => {
    if (this.shouldRefresh()) {
      const requestURL = new URL(OfflineWeatherService.apiURL);
      requestURL.searchParams.set('APPID', this.characteristics.apiKey);
      requestURL.searchParams.set('callback', this.callbackKey);
      requestURL.searchParams.set('q', this.characteristics.city);
      requestURL.searchParams.set('units', this.characteristics.units);

      const scriptElement = document.createElement('script');
      scriptElement.async = true;
      scriptElement.src = requestURL.toString();
      scriptElement.type = 'text/javascript';

      this.lastScriptElement = scriptElement;
      document.body.appendChild(this.lastScriptElement);
    }
  }
}
