"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = exports.getDefaults = exports.associateDevice = exports.generateDeviceId = exports.getConn = void 0;
var axios_1 = require("axios");
var simulator_1 = require("./simulator");
var Log_1 = require("./models/Log");
var cache = require('ez-cache')();
var conn;
exports.getConn = function (apiHost, apiKey, verbose) {
    if (!conn) {
        // create a connection to the device API
        conn = axios_1.default.create({
            baseURL: apiHost,
            headers: {
                Authorization: "Bearer " + apiKey,
                'Content-Type': 'text/plain',
            },
        });
        conn.interceptors.request.use(function (config) {
            new Log_1.Log(!!verbose).debug(config);
            return config;
        });
    }
    return conn;
};
exports.generateDeviceId = function () {
    return "nrfsim-" + Math.floor(Math.random() * 1000000000000000000000);
};
exports.associateDevice = function (_a) {
    var deviceId = _a.deviceId, deviceOwnershipCode = _a.deviceOwnershipCode, apiHost = _a.apiHost, apiKey = _a.apiKey, verbose = _a.verbose;
    return exports.getConn(apiHost, apiKey, !!verbose).put("/v1/association/" + deviceId, deviceOwnershipCode);
};
exports.getDefaults = function (_a) {
    var deviceId = _a.deviceId, endpoint = _a.endpoint, mqttMessagesPrefix = _a.mqttMessagesPrefix, certsResponse = _a.certsResponse, apiHost = _a.apiHost, apiKey = _a.apiKey, deviceOwnershipCode = _a.deviceOwnershipCode, verbose = _a.verbose;
    return __awaiter(void 0, void 0, void 0, function () {
        var conn, log, defaults, cacheFile, cachedDefaults, _b, defaultEndpoint, defaultMqttMessagesPrefix, data, defaultJsonCert, data, tenantId, data;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    conn = exports.getConn(apiHost, apiKey, !!verbose);
                    log = new Log_1.Log(!!verbose);
                    defaults = {
                        endpoint: endpoint || '',
                        mqttMessagesPrefix: mqttMessagesPrefix || '',
                        certsResponse: certsResponse || '',
                        tenantId: '',
                    };
                    cacheFile = cache.getFilePath(deviceId);
                    if (!cache.exists(cacheFile)) return [3 /*break*/, 2];
                    return [4 /*yield*/, cache.get(cacheFile)];
                case 1:
                    _b = _c.sent();
                    return [3 /*break*/, 3];
                case 2:
                    _b = {};
                    _c.label = 3;
                case 3:
                    cachedDefaults = _b;
                    if (!!(endpoint && mqttMessagesPrefix)) return [3 /*break*/, 6];
                    log.debug("Grabbing mqttEndpoint and messagesPrefix...");
                    defaultEndpoint = cachedDefaults.endpoint || '', defaultMqttMessagesPrefix = cachedDefaults.mqttMessagesPrefix || '';
                    if (!!(defaultEndpoint && defaultMqttMessagesPrefix)) return [3 /*break*/, 5];
                    log.debug('Fetching endpoints from device API.\n');
                    return [4 /*yield*/, conn.get("/v1/account")];
                case 4:
                    data = (_c.sent()).data;
                    defaultMqttMessagesPrefix = data.mqttTopicPrefix + 'm/';
                    defaultEndpoint = data.mqttEndpoint;
                    _c.label = 5;
                case 5:
                    if (!endpoint) {
                        defaults.endpoint = defaultEndpoint;
                    }
                    if (!mqttMessagesPrefix) {
                        defaults.mqttMessagesPrefix = defaultMqttMessagesPrefix;
                    }
                    _c.label = 6;
                case 6:
                    if (!!certsResponse) return [3 /*break*/, 9];
                    log.debug('Grabbing cert...');
                    defaultJsonCert = cachedDefaults.certsResponse || '';
                    if (!!defaultJsonCert) return [3 /*break*/, 8];
                    log.debug('Fetching cert from device API.\n');
                    return [4 /*yield*/, conn.post("/v1/devices/" + deviceId + "/certificates", deviceOwnershipCode)];
                case 7:
                    data = (_c.sent()).data;
                    defaultJsonCert = JSON.stringify(data);
                    _c.label = 8;
                case 8:
                    defaults.certsResponse = defaultJsonCert;
                    _c.label = 9;
                case 9:
                    tenantId = defaults.mqttMessagesPrefix.split('/')[1];
                    if (!!tenantId) return [3 /*break*/, 11];
                    return [4 /*yield*/, conn.get("/v1/account")];
                case 10:
                    data = (_c.sent()).data;
                    tenantId = data.team.tenantId;
                    _c.label = 11;
                case 11:
                    if (!tenantId) {
                        throw new Error("Cannot continue without tenantId! defaults: " + JSON.stringify(defaults));
                    }
                    defaults.tenantId = tenantId;
                    return [4 /*yield*/, cache.set(cacheFile, defaults)];
                case 12:
                    _c.sent();
                    return [2 /*return*/, defaults];
            }
        });
    });
};
exports.run = function (config) { return __awaiter(void 0, void 0, void 0, function () {
    var deviceId, apiKey, apiHost, certsResponse, endpoint, mqttMessagesPrefix, deviceOwnershipCode, associate, verbose, log, defaults;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0:
                deviceId = config.deviceId, apiKey = config.apiKey, apiHost = config.apiHost, certsResponse = config.certsResponse, endpoint = config.endpoint, mqttMessagesPrefix = config.mqttMessagesPrefix, deviceOwnershipCode = config.deviceOwnershipCode, associate = config.associate, verbose = config.verbose;
                log = new Log_1.Log(!!verbose);
                config.deviceId = deviceId || exports.generateDeviceId();
                // grab the defaults from the API
                if (!(apiKey && apiHost)) {
                    log.error("ERROR: apiKey: (passed val: \"" + apiKey + "\") and apiHost (passed val: \"" + apiHost + "\") are required");
                    return [2 /*return*/];
                }
                log.debug(log.prettify('INITIAL CONFIG', [
                    ['DEVICE ID', config.deviceId],
                    ['DEVICE PIN', config.deviceOwnershipCode],
                    ['API HOST', config.apiHost],
                    ['API KEY', config.apiKey],
                    ['TENANT ID', config.tenantId],
                    ['STAGE', config.stage],
                ]));
                return [4 /*yield*/, exports.getDefaults({
                        deviceId: config.deviceId,
                        deviceOwnershipCode: deviceOwnershipCode,
                        mqttMessagesPrefix: mqttMessagesPrefix,
                        certsResponse: certsResponse,
                        endpoint: endpoint,
                        apiHost: apiHost,
                        apiKey: apiKey,
                        verbose: verbose,
                    })];
            case 1:
                defaults = _a.sent();
                config.certsResponse = defaults.certsResponse;
                config.mqttMessagesPrefix = defaults.mqttMessagesPrefix;
                config.endpoint = defaults.endpoint;
                config.tenantId = defaults.tenantId;
                log.info(log.prettify('CONFIG', [
                    ['DEVICE ID', config.deviceId],
                    ['DEVICE PIN', config.deviceOwnershipCode],
                    ['API HOST', config.apiHost],
                    ['API KEY', config.apiKey],
                    ['TENANT ID', config.tenantId],
                    ['STAGE', config.stage],
                ]));
                log.success('starting simulator...');
                if (associate) {
                    config.onConnect = function (deviceId) { return __awaiter(void 0, void 0, void 0, function () {
                        var err_1;
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0:
                                    log.info("ATTEMPTING TO ASSOCIATE " + config.deviceId + " WITH API KEY " + config.apiKey + " VIA " + config.apiHost);
                                    // wait to ensure the device is available in AWS IoT so it can be associated
                                    return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
                                case 1:
                                    // wait to ensure the device is available in AWS IoT so it can be associated
                                    _a.sent();
                                    _a.label = 2;
                                case 2:
                                    _a.trys.push([2, 4, , 5]);
                                    return [4 /*yield*/, exports.associateDevice({
                                            deviceId: deviceId,
                                            deviceOwnershipCode: deviceOwnershipCode,
                                            apiHost: apiHost,
                                            apiKey: apiKey,
                                            verbose: verbose,
                                        })];
                                case 3:
                                    _a.sent();
                                    log.success('DEVICE ASSOCIATED!');
                                    return [3 /*break*/, 5];
                                case 4:
                                    err_1 = _a.sent();
                                    log.error("Failed to associate: " + err_1);
                                    return [3 /*break*/, 5];
                                case 5: return [2 /*return*/];
                            }
                        });
                    }); };
                }
                simulator_1.simulator(config).catch(function (err) {
                    log.error(err);
                });
                return [2 /*return*/];
        }
    });
}); };
