text: "use strict";
  var __assign = (this && this.__assign) || function () {
      __assign = Object.assign || function(t) {
          for (var s, i = 1, n = arguments.length; i < n; i++) {
              s = arguments[i];
              for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                  t[p] = s[p];
          }
          return t;
      };
      return __assign.apply(this, arguments);
  };
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
      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) : new P(function (resolve) { resolve(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 });
  var http = require("http");
  var url = require("url");
  /**
   * Authentication with Google's APIs.
   */
  var google_auth_library_1 = require("google-auth-library");
  var googleapis_1 = require("googleapis");
  var inquirer_1 = require("inquirer");
  var dotfile_1 = require("./dotfile");
  var manifest_1 = require("./manifest");
  var utils_1 = require("./utils");
  var open = require("opn");
  var readline = require("readline");
  // Auth is complicated. Consider yourself warned.
  // tslint:disable:max-line-length
  // GLOBAL: clasp login will store this (~/.clasprc.json):
  // {
  //   "access_token": "XXX",
  //   "refresh_token": "1/k4rt_hgxbeGdaRag2TSVgnXgUrWcXwerPpvlzGG1peHVfzI58EZH0P25c7ykiRYd",
  //   "scope": "https://www.googleapis.com/auth/script.projects https://www.googleapis.com/auth/script ...",
  //   "token_type": "Bearer",
  //   "expiry_date": 1539130731398
  // }
  // LOCAL: clasp login will store this (./.clasprc.json):
  // {
  //   "token": {
  //     "access_token": "XXX",
  //     "refresh_token": "1/k4rw_hgxbeGdaRag2TSVgnXgUrWcXwerPpvlzGG1peHVfzI58EZH0P25c7ykiRYd",
  //     "scope": "https://www.googleapis.com/auth/script.projects https://www.googleapis.com/auth/script ...",
  //     "token_type": "Bearer",
  //     "expiry_date": 1539130731398
  //   },
  //   // Settings
  //   "oauth2ClientSettings": {
  //     "clientId": "807925367021-infvb16rd7lasqi22q2npeahkeodfrq5.apps.googleusercontent.com",
  //     "clientSecret": "9dbdeOCRHUyriewCoDrLHtPg",
  //     "redirectUri": "http://localhost"
  //   },
  //   "isLocalCreds": true
  // }
  // API settings
  // @see https://developers.google.com/oauthplayground/
  var REDIRECT_URI_OOB = 'urn:ietf:wg:oauth:2.0:oob';
  var oauth2ClientAuthUrlOpts = {
      access_type: 'offline',
      scope: [
          'https://www.googleapis.com/auth/script.deployments',
          'https://www.googleapis.com/auth/script.projects',
          'https://www.googleapis.com/auth/script.webapp.deploy',
          'https://www.googleapis.com/auth/drive.metadata.readonly',
          'https://www.googleapis.com/auth/drive.file',
          'https://www.googleapis.com/auth/service.management',
          'https://www.googleapis.com/auth/logging.read',
          // Extra scope since service.management doesn't work alone
          'https://www.googleapis.com/auth/cloud-platform',
      ],
  };
  var globalOauth2ClientSettings = {
      clientId: '1072944905499-vm2v2i5dvn0a0d2o4ca36i1vge8cvbn0.apps.googleusercontent.com',
      clientSecret: 'v6V3fKV_zWU7iw1DrpO1rknX',
      redirectUri: 'http://localhost',
  };
  var globalOAuth2Client = new google_auth_library_1.OAuth2Client(globalOauth2ClientSettings);
  var localOAuth2Client; // Must be set up after authorize.
  // *Global* Google API clients
  exports.script = googleapis_1.google.script({ version: 'v1', auth: globalOAuth2Client });
  exports.logger = googleapis_1.google.logging({ version: 'v2', auth: globalOAuth2Client });
  exports.drive = googleapis_1.google.drive({ version: 'v3', auth: globalOAuth2Client });
  exports.discovery = googleapis_1.google.discovery({ version: 'v1' });
  exports.serviceUsage = googleapis_1.google.serviceusage({
      version: 'v1',
      auth: globalOAuth2Client,
  });
  /**
   * Gets the local OAuth client for the Google Apps Script API.
   * Only the Apps Script API needs to use local credential for the Execution API (script.run).
   * @see https://developers.google.com/apps-script/api/how-tos/execute
   */
  function getLocalScript() {
      return __awaiter(this, void 0, void 0, function () {
          return __generator(this, function (_a) {
              return [2 /*return*/, googleapis_1.google.script({ version: 'v1', auth: localOAuth2Client })];
          });
      });
  }
  exports.getLocalScript = getLocalScript;
  /**
   * Requests authorization to manage Apps Script projects.
   * @param {boolean} useLocalhost Uses a local HTTP server if true. Manual entry o.w.
   * @param {ClaspCredentials?} creds An optional credentials object.
   * @param {string[]} [additionalScopes=[]] authorize additional OAuth scopes.
   */
  function authorize(options) {
      return __awaiter(this, void 0, void 0, function () {
          var creds, oAuth2ClientOptions, localOAuth2ClientOptions, globalOauth2ClientOptions, token, claspToken, err_1;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      creds = options.creds;
                      _a.label = 1;
                  case 1:
                      _a.trys.push([1, 7, , 8]);
                      // Add custom scopes when authing.
                      oauth2ClientAuthUrlOpts.scope = oauth2ClientAuthUrlOpts.scope.concat((options.additionalScopes || []));
                      oAuth2ClientOptions = void 0;
                      if (options.creds) {
                          // if we passed our own creds
                          // Use local credentials
                          console.log(utils_1.LOG.CREDS_FROM_PROJECT(options.creds.installed.project_id));
                          localOAuth2ClientOptions = {
                              clientId: options.creds.installed.client_id,
                              clientSecret: options.creds.installed.client_secret,
                              redirectUri: options.creds.installed.redirect_uris[0],
                          };
                          oAuth2ClientOptions = localOAuth2ClientOptions;
                      }
                      else {
                          globalOauth2ClientOptions = {
                              clientId: '1072944905499-vm2v2i5dvn0a0d2o4ca36i1vge8cvbn0.apps.googleusercontent.com',
                              clientSecret: 'v6V3fKV_zWU7iw1DrpO1rknX',
                              redirectUri: 'http://localhost',
                          };
                          oAuth2ClientOptions = globalOauth2ClientOptions;
                      }
                      return [4 /*yield*/, (options.useLocalhost
                              ? authorizeWithLocalhost(oAuth2ClientOptions)
                              : authorizeWithoutLocalhost(oAuth2ClientOptions))];
                  case 2:
                      token = _a.sent();
                      console.log(utils_1.LOG.AUTH_SUCCESSFUL + '\n');
                      claspToken = void 0;
                      if (!creds) return [3 /*break*/, 4];
                      // Save local ClaspCredentials.
                      claspToken = {
                          token: token,
                          oauth2ClientSettings: {
                              clientId: creds.installed.client_id,
                              clientSecret: creds.installed.client_secret,
                              redirectUri: creds.installed.redirect_uris[0],
                          },
                          isLocalCreds: true,
                      };
                      return [4 /*yield*/, dotfile_1.DOTFILE.RC_LOCAL().write(claspToken)];
                  case 3:
                      _a.sent();
                      return [3 /*break*/, 6];
                  case 4:
                      // Save global ClaspCredentials.
                      claspToken = {
                          token: token,
                          oauth2ClientSettings: globalOauth2ClientSettings,
                          isLocalCreds: false,
                      };
                      return [4 /*yield*/, dotfile_1.DOTFILE.RC.write(claspToken)];
                  case 5:
                      _a.sent();
                      _a.label = 6;
                  case 6:
                      console.log(utils_1.LOG.SAVED_CREDS(!!creds));
                      return [3 /*break*/, 8];
                  case 7:
                      err_1 = _a.sent();
                      utils_1.logError(null, utils_1.ERROR.ACCESS_TOKEN + err_1);
                      return [3 /*break*/, 8];
                  case 8: return [2 /*return*/];
              }
          });
      });
  }
  exports.authorize = authorize;
  /**
   * Loads the Apps Script API credentials for the CLI.
   * Required before every API call.
   */
  function loadAPICredentials() {
      return __awaiter(this, void 0, void 0, function () {
          var rc;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, utils_1.getOAuthSettings()];
                  case 1:
                      rc = _a.sent();
                      return [4 /*yield*/, setOauthCredentials(rc)];
                  case 2:
                      _a.sent();
                      return [2 /*return*/, rc];
              }
          });
      });
  }
  exports.loadAPICredentials = loadAPICredentials;
  /**
   * Requests authorization to manage Apps Script projects. Spins up
   * a temporary HTTP server to handle the auth redirect.
   * @param {OAuth2ClientOptions} oAuth2ClientOptions The required client options for auth
   * Used for local/global testing.
   */
  function authorizeWithLocalhost(oAuth2ClientOptions) {
      return __awaiter(this, void 0, void 0, function () {
          var server, port, client, authCode;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0: return [4 /*yield*/, new Promise(function (resolve, _) {
                          var s = http.createServer();
                          s.listen(0, function () { return resolve(s); });
                      })];
                  case 1:
                      server = _a.sent();
                      port = server.address().port;
                      client = new google_auth_library_1.OAuth2Client(__assign({}, oAuth2ClientOptions, { redirectUri: "http://localhost:" + port }));
                      return [4 /*yield*/, new Promise(function (res, rej) {
                              server.on('request', function (req, resp) {
                                  var urlParts = url.parse(req.url || '', true);
                                  if (urlParts.query.code) {
                                      res(urlParts.query.code);
                                  }
                                  else {
                                      rej(urlParts.query.error);
                                  }
                                  resp.end(utils_1.LOG.AUTH_PAGE_SUCCESSFUL);
                              });
                              var authUrl = client.generateAuthUrl(oauth2ClientAuthUrlOpts);
                              console.log(utils_1.LOG.AUTHORIZE(authUrl));
                              open(authUrl);
                          })];
                  case 2:
                      authCode = _a.sent();
                      server.close();
                      return [4 /*yield*/, client.getToken(authCode)];
                  case 3: return [2 /*return*/, (_a.sent()).tokens];
              }
          });
      });
  }
  /**
   * Requests authorization to manage Apps Script projects. Requires the user to
   * manually copy/paste the authorization code. No HTTP server is used.
   * @param {OAuth2ClientOptions} oAuth2ClientOptions The required client options for auth.
   */
  function authorizeWithoutLocalhost(oAuth2ClientOptions) {
      return __awaiter(this, void 0, void 0, function () {
          var client, authUrl, authCode;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      client = new google_auth_library_1.OAuth2Client(__assign({}, oAuth2ClientOptions, { redirectUri: REDIRECT_URI_OOB }));
                      authUrl = client.generateAuthUrl(oauth2ClientAuthUrlOpts);
                      console.log(utils_1.LOG.AUTHORIZE(authUrl));
                      return [4 /*yield*/, new Promise(function (res, rej) {
                              var rl = readline.createInterface({
                                  input: process.stdin,
                                  output: process.stdout,
                              });
                              rl.question(utils_1.LOG.AUTH_CODE, function (code) {
                                  if (code && code.length) {
                                      res(code);
                                  }
                                  else {
                                      rej('No authorization code entered.');
                                  }
                                  rl.close();
                              });
                          })];
                  case 1:
                      authCode = _a.sent();
                      return [4 /*yield*/, client.getToken(authCode)];
                  case 2: return [2 /*return*/, (_a.sent()).tokens];
              }
          });
      });
  }
  /**
   * Set global OAuth client credentails from rc, save new if access token refreshed.
   * @param {ClaspToken} rc OAuth client settings from rc file.
   */
  function setOauthCredentials(rc) {
      return __awaiter(this, void 0, void 0, function () {
          /**
           * Refreshes the credentials and saves them.
           */
          function refreshCredentials(oAuthClient) {
              return __awaiter(this, void 0, void 0, function () {
                  var oldExpiry;
                  return __generator(this, function (_a) {
                      switch (_a.label) {
                          case 0:
                              oldExpiry = oAuthClient.credentials.expiry_date || 0;
                              return [4 /*yield*/, oAuthClient.getAccessToken()];
                          case 1:
                              _a.sent(); // refreshes expiry date if required
                              if (oAuthClient.credentials.expiry_date === oldExpiry)
                                  return [2 /*return*/];
                              rc.token = oAuthClient.credentials;
                              return [2 /*return*/];
                      }
                  });
              });
          }
          var err_2;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      _a.trys.push([0, 6, , 7]);
                      return [4 /*yield*/, utils_1.checkIfOnline()];
                  case 1:
                      _a.sent();
                      if (!rc.isLocalCreds) return [3 /*break*/, 3];
                      localOAuth2Client = new google_auth_library_1.OAuth2Client({
                          clientId: rc.oauth2ClientSettings.clientId,
                          clientSecret: rc.oauth2ClientSettings.clientSecret,
                          redirectUri: rc.oauth2ClientSettings.redirectUri,
                      });
                      localOAuth2Client.setCredentials(rc.token);
                      return [4 /*yield*/, refreshCredentials(localOAuth2Client)];
                  case 2:
                      _a.sent();
                      _a.label = 3;
                  case 3:
                      // Always use the global credentials too for non-run functions.
                      globalOAuth2Client.setCredentials(rc.token);
                      return [4 /*yield*/, refreshCredentials(globalOAuth2Client)];
                  case 4:
                      _a.sent();
                      // Save the credentials.
                      return [4 /*yield*/, (rc.isLocalCreds ? dotfile_1.DOTFILE.RC_LOCAL() : dotfile_1.DOTFILE.RC).write(rc)];
                  case 5:
                      // Save the credentials.
                      _a.sent();
                      return [3 /*break*/, 7];
                  case 6:
                      err_2 = _a.sent();
                      utils_1.logError(null, utils_1.ERROR.ACCESS_TOKEN + err_2);
                      return [3 /*break*/, 7];
                  case 7: return [2 /*return*/];
              }
          });
      });
  }
  /**
   * Compare global OAuth client scopes against manifest and prompt user to
   * authorize if new scopes found (local OAuth credentails only).
   * @param {ClaspToken} rc OAuth client settings from rc file.
   */
  function checkOauthScopes(rc) {
      return __awaiter(this, void 0, void 0, function () {
          var scopes_1, oauthScopes, newScopes_1, err_3;
          var _this = this;
          return __generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      _a.trys.push([0, 6, , 7]);
                      return [4 /*yield*/, utils_1.checkIfOnline()];
                  case 1:
                      _a.sent();
                      return [4 /*yield*/, setOauthCredentials(rc)];
                  case 2:
                      _a.sent();
                      return [4 /*yield*/, globalOAuth2Client.getTokenInfo(globalOAuth2Client.credentials
                              .access_token)];
                  case 3:
                      scopes_1 = (_a.sent()).scopes;
                      return [4 /*yield*/, manifest_1.readManifest()];
                  case 4:
                      oauthScopes = (_a.sent()).oauthScopes;
                      newScopes_1 = oauthScopes && oauthScopes.length ? oauthScopes.filter(function (x) { return !scopes_1.includes(x); }) : [];
                      if (!newScopes_1.length)
                          return [2 /*return*/];
                      console.log('New authoization scopes detected in manifest:\n', newScopes_1);
                      return [4 /*yield*/, inquirer_1.prompt([
                              {
                                  type: 'confirm',
                                  name: 'doAuth',
                                  message: 'Authorize new scopes?',
                              },
                              {
                                  type: 'confirm',
                                  name: 'localhost',
                                  message: 'Use localhost?',
                                  when: function (answers) {
                                      return answers.doAuth;
                                  },
                              },
                          ]).then(function (answers) { return __awaiter(_this, void 0, void 0, function () {
                              return __generator(this, function (_a) {
                                  switch (_a.label) {
                                      case 0:
                                          if (!answers.doAuth) return [3 /*break*/, 2];
                                          if (!rc.isLocalCreds)
                                              return [2 /*return*/, utils_1.logError(null, utils_1.ERROR.NO_LOCAL_CREDENTIALS)];
                                          return [4 /*yield*/, authorize({
                                                  useLocalhost: answers.localhost,
                                                  additionalScopes: newScopes_1,
                                              })];
                                      case 1:
                                          _a.sent();
                                          _a.label = 2;
                                      case 2: return [2 /*return*/];
                                  }
                              });
                          }); })];
                  case 5:
                      _a.sent();
                      return [3 /*break*/, 7];
                  case 6:
                      err_3 = _a.sent();
                      utils_1.logError(null, utils_1.ERROR.BAD_REQUEST(err_3.message));
                      return [3 /*break*/, 7];
                  case 7: return [2 /*return*/];
              }
          });
      });
  }
  exports.checkOauthScopes = checkOauthScopes;
  