// import Observable from 'zen-observable';
// import MqttPattern from 'mqtt-pattern';
// import AWS from 'aws-sdk/global';
// import getCredentials, { makeToken } from '../../common/auth/credentials';
// import TokenValidator from '../../common/auth/token';
// import { device } from 'aws-iot-device-sdk';
// import Config from '../../config';
// import { iPubSub } from '../../common/pubsub';
// import { IDebug } from 'debug';
// const debug: IDebug = require('debug')('iot');
// debug('iot module loaded');

// export interface IotConfig {
//   clientId: string;
//   region?: string;
//   prefix?: string;
//   credentials: AWS.Credentials;
//   host: string;
// }

// export interface IotProvider {
//   clientId: string;
//   host: string;
//   region: string;
//   prefix: string;
// }

// export class IotProvider implements iPubSub {
//   clientId: string;
//   host: string;
//   region: string;
//   private _client: device;
//   prefix: string;
//   private credentials: AWS.Credentials;
//   constructor(config: IotConfig) {
//     Object.assign(this, this.processConfig(config));
//     this.addPrefix = this.addPrefix.bind(this);
//   }

//   static async fromToken(token: string, prefix?: string) {
//     const validator = new TokenValidator(token);
//     const credentials = await getCredentials(token, 'us-west-2');
//     return new IotProvider({
//       clientId: ['bot', validator.developerUserId].join('/'),
//       region: Config.aws.region,
//       prefix: '',
//       host: Config.iot.host,
//       credentials
//     });
//   }

//   get client(): Promise<device> | device {
//     if (this._client) {
//       return this._client;
//     }
//     return this.newClient(this.clientId)
//       .then(device => {
//         this._client = device;
//         return device;
//       })
//       .catch(e => {
//         console.error('Problem setting client', e);
//         throw new Error(e);
//       });
//   }

//   static async fromUsername(username: string, prefix: string) {
//     const tokenResponse = await makeToken(username, 900, []);
//     return IotProvider.fromToken(tokenResponse.Token, prefix);
//   }

//   configure(obj: IotConfig) {
//     Object.assign(this, obj);
//     this.prefix = obj.prefix || this.clientId;
//   }

//   processConfig(cfg: IotConfig): IotConfig {
//     const { clientId, host, prefix, credentials, region } = cfg;

//     return {
//       clientId,
//       host,
//       prefix,
//       credentials,
//       region
//     };
//   }

//   public async newClient(clientId): Promise<device> {
//     const { host, region } = this;
//     await this.credentials.getPromise();
//     const { accessKeyId, secretAccessKey: secretKey, sessionToken } = this.credentials;
//     const client = new device({
//       region: region,
//       host: host,
//       debug: false,
//       clientId: clientId,
//       protocol: 'wss',
//       maximumReconnectTimeMs: 8000,
//       accessKeyId,
//       secretKey,
//       sessionToken
//     });
//     // client.trace = (...args) => console.debug(clientId, ...args);
//     client.on('message', (topic, msg) => this._onMessage(topic, msg.toString()));
//     client.on('close', console.log.bind(console, 'Closing client: ' + clientId));
//     client.on('error', console.error.bind(console, 'Error with client: ' + clientId));
//     client.on('reconnect', console.log.bind(console, 'Reconnecting to client: ' + clientId));
//     await new Promise((resolve, reject) => {
//       client.once('connect', resolve);
//       client.once('error', reject);
//     }).catch(e => console.log('Client error: ', e));

//     return client;
//   }
//   protected async connect(clientId: string): Promise<device> {
//     return await this.client;
//   }

//   async publish(topics: string[] | string, msg: any): Promise<boolean> {
//     const targetTopics = ([] as string[]).concat(topics).map(this.addPrefix);
//     const message = JSON.stringify(msg);

//     const client = await this.client;

//     console.debug('Publishing to topic(s)', targetTopics.join(','), message);
//     targetTopics.forEach(topic => client.publish(topic, message));
//     return true;
//   }

//   protected async disconnect(): Promise<any> {
//     const client = await this.client;
//     return new Promise((resolve, reject) => {
//       client.end(false, resolve);
//     });
//   }

//   stripPrefix(topic) {
//     return topic
//       .split(this.prefix + '/')
//       .slice(1)
//       .join('');
//   }

//   addPrefix(topic: string): string {
//     if (!this.prefix || !this.prefix.length || topic.startsWith(this.prefix)) {
//       return topic;
//     }
//     return [this.prefix, topic].filter(v => v && v.length).join('/');
//   }

//   private _topicObservers: Map<string, Set<ZenObservable.SubscriptionObserver<any>>> = new Map();

//   private _onMessage(topic: string, msg: any) {
//     try {
//       console.log('Message on topic', topic, msg);
//       const observersForTopic = new Set();
//       // @ts-ignore
//       for (let [topicPattern, topicSet] of this._topicObservers.entries()) {
//         if (MqttPattern.matches(topicPattern, topic)) {
//           for (let obs of topicSet) {
//             observersForTopic.add(obs);
//           }
//         }
//       }

//       const parsedMessage = JSON.parse(msg);
//       console.log(observersForTopic);
//       observersForTopic.forEach(observer => observer.next(parsedMessage));
//     } catch (error) {
//       console.log('Error handling message', error, msg);
//     }
//   }

//   observe(topics: string[] | string): Observable<any> {
//     const targetTopics = ([] as string[]).concat(topics).map(this.addPrefix);
//     // console.log('Subscribing to topics', targetTopics.join(', '))
//     return new Observable(observer => {
//       targetTopics.forEach(topic => {
//         let observersForTopic = this._topicObservers.get(topic);

//         if (!observersForTopic) {
//           observersForTopic = new Set();

//           this._topicObservers.set(topic, observersForTopic);
//         }

//         observersForTopic.add(observer);
//       });

//       let client: device;
//       (async () => {
//         client = await this.client;
//         targetTopics.forEach(topic => {
//           let result = client.subscribe(topic);
//           console.log('Subscribed to topic', topic, result);
//         });
//       })();

//       return () => {
//         console.debug('Unsubscribing from topic(s)', targetTopics.join(','));

//         if (client) {
//           targetTopics.forEach(topic => {
//             client.unsubscribe(topic);

//             const observersForTopic =
//               this._topicObservers.get(topic) || (new Set() as Set<ZenObservable.SubscriptionObserver<any>>);

//             observersForTopic.forEach(observer => observer.complete());

//             observersForTopic.clear();
//           });

//           this.disconnect();
//         }

//         return null;
//       };
//     });
//   }
// }
