import { computed } from '@ember/object';
import Service, { inject as service } from '@ember/service';
import { LRUMap } from 'lru_map';
import NodeStatsTracker from 'nomad-ui/utils/classes/node-stats-tracker';
import AllocationStatsTracker from 'nomad-ui/utils/classes/allocation-stats-tracker';

// An unbounded number of stat trackers is a great way to gobble up all the memory
// on a machine. This max number is unscientific, but aims to balance losing
// stat trackers a user is likely to return to with preventing gc from freeing
// memory occupied by stat trackers a user is likely to no longer care about
const MAX_STAT_TRACKERS = 10;
let registry;

const exists = (tracker, prop) =>
  tracker.get(prop) &&
  !tracker.get(prop).isDestroyed &&
  !tracker.get(prop).isDestroying;

export default class StatsTrackersRegistryService extends Service {
  @service token;

  constructor() {
    super(...arguments);

    // The LRUMap limits the number of trackers tracked by making room for
    // new entries beyond the limit by removing the least recently used entry.
    registry = new LRUMap(MAX_STAT_TRACKERS);
  }

  // A read-only way of getting a reference to the registry.
  // Since this could be overwritten by a bad actor, it isn't
  // used in getTracker
  @computed
  get registryRef() {
    return registry;
  }

  getTracker(resource) {
    if (!resource) return;

    const type = resource && resource.constructor.modelName;
    const key = `${type}:${resource.get('id')}`;
    const Constructor =
      type === 'node' ? NodeStatsTracker : AllocationStatsTracker;
    const resourceProp = type === 'node' ? 'node' : 'allocation';

    const cachedTracker = registry.get(key);
    if (cachedTracker) {
      // It's possible for the resource on a cachedTracker to have been
      // deleted. Rebind it if that's the case.
      if (!exists(cachedTracker, resourceProp))
        cachedTracker.set(resourceProp, resource);
      return cachedTracker;
    }

    const tracker = Constructor.create({
      fetch: (url) => this.token.authorizedRequest(url),
      [resourceProp]: resource,
    });

    registry.set(key, tracker);

    return tracker;
  }
}
