import { TagCache, Taggers } from './interfaces/tagcache';
import { SensuTagSet } from './interfaces/sensu';
import AWS = require('aws-sdk');
import * as Cachers from './aws/cachers/index';

export class AWSTags {
  refreshInterval: number;
  tagCache: TagCache;
  taggableNamespaces: Taggers;

  private static _instance: AWSTags;
  constructor() {
    if (AWSTags._instance) {
      throw new Error("Can't use 'new' on a singleton, use AWSTags.init instead.")
    }
    this.taggableNamespaces = {
      "AWS/ApplicationELB": new Cachers.ALBTags(),
      "AWS/AutoScaling": new Cachers.ASGTags(),
      "AWS/EC2": new Cachers.EC2Tags(),
      "AWS/EBS": new Cachers.EBSTags(),
      "AWS/ElastiCache": new Cachers.ElastiCacheTags(),
      "AWS/ES": new Cachers.ESTags(),
      "AWS/Kinesis": new Cachers.KinesisTags(),
      "AWS/Lambda": new Cachers.LambdaTags(),
      "AWS/RDS": new Cachers.RDSTags(),
      "AWS/S3": new Cachers.S3Tags(),
    };
    this.refreshInterval = 900000;

    this.tagCache = {};
    for (let ns of Object.keys(this.taggableNamespaces)) {
      this.tagCache[ns] = { LastUpdated: new Date(0), Dimension: {} };
    }
  }

  public static get init(): AWSTags {
    if (AWSTags._instance) {
      return AWSTags._instance;
    }
    AWSTags._instance = new AWSTags();
    return AWSTags._instance;
  }

  public async fillCaches() {
    let promises = [];
    for (let ns of Object.keys(this.taggableNamespaces)) {

      if (this.tagCache[ns].LastUpdated.getTime() > (Date.now() - this.refreshInterval)) {
        console.log("Skipping cache update for " + ns + " since we're within the TTL");
        continue;
      }

      console.log("Running AWS Tag Cacher for " + ns)
      promises.push(this.fillNamespaceCache(ns));
    }
    return Promise.all(promises);
  }

  public getTags(namespace: string, dimensions: AWS.CloudWatch.Types.Dimensions): SensuTagSet {
    if (!((Object.keys(this.taggableNamespaces)).indexOf(namespace) > -1)) {
      throw new Error("You've asked for an impossible cache namespace: " + namespace);
    }

    return (this.taggableNamespaces[namespace]).get(dimensions, this.tagCache[namespace]);
  }

  public async fillNamespaceCache(namespace: string) {
    if (!((Object.keys(this.taggableNamespaces)).indexOf(namespace) > -1)) {
      throw new Error("You've asked for an impossible cache namespace: " + namespace);
    }

    return (this.taggableNamespaces[namespace]).fill().then(c => {
      this.tagCache[namespace] = c;
      console.log("Updated " + namespace + " tag caches successfully");
    });
  }
}
