text: const env = require('node-env-file')
  const fetch = require('fetch').fetchUrl
  const path = require('path')
  const exphbs = require("express-handlebars");
  const express = require('express')
  const session = require('express-session')
  const DataProvider = new require('./dataprovider')
  const google = require('googleapis')
  const googleAuth = require('google-auth-library')
  const bodyParser = require('body-parser')
  const SCOPES = ['https://www.googleapis.com/auth/youtube']
  
  try {
      env(path.join(__dirname, '.env'));
  } catch (e) {}
  
  const youtubeApiRequest = (endpoint, access_token, params) => {
      return new Promise((resolve, reject) => {
          let url = `https://www.googleapis.com/youtube/v3${endpoint}?part=snippet%2Cstatus&key=${process.env.YOUTUBE_APIKEY}`
          let options = {
              'method': 'post', 
              'headers': {
                  'Authorization': `Bearer ${access_token}`,
                  'Content-Type': 'application/json'
              },
              'payload': JSON.stringify(params)
          }
          fetch(url,options, (err, meta, data) => {
              if (meta.status === 200) {
                  resolve({
                      'meta': meta,
                      'data': JSON.parse(data.toString())
                  })
              } else {
                  reject(err)
              }
          })
      })
  }
  
  /**
   * Utility function to snip sensitive data to make it suitable 
   * for logging.
   * 
   * @param {*} input 
   */
  const snip = function(input) {
      if (!input || input.length < 3) return 'too short to snip'
      if (input.length < 6) return `${input.substring(0,3)}...`
      return `${input.substring(0,3)}...${input.substring(input.length-3)}`
  }
  
  const dataprovider = new DataProvider()
  const app = express()
  app.use(bodyParser.json())
  app.engine('handlebars', exphbs({defaultLayout: 'main'}))
  app.set('view engine', 'handlebars')
  app.use(session({
      'saveUninitialized': true,
      'resave': true,
      'secret': process.env.SESSION_SECRET || 'kdsfh23748urweifhsdiw47382qyrwfhoedslc'
  }))
  
  app.get("/google394c6f98ea2f64d0.html", (req, res) => {
      res.type("text/html");
      res.send("google-site-verification: google394c6f98ea2f64d0.html");
  })
  
  app.get('/', (req, res) => {
      // home
      res.render("home")
  })
  app.get('/oauth2/callback', (req, res) => {
      // get authcode from url
      let authcode = req.query.code
      console.log(`Retrieved authcode ${snip(authcode)} from URL - attempting to exchange for access token`)
      
      // new client
      let auth = new googleAuth();
      let oauth2Client = new auth.OAuth2(process.env.OAUTH_CLIENT_ID, 
          process.env.OAUTH_CLIENT_SECRET, 
          process.env.OAUTH_REDIRECT_URI)
  
      // exchange
      oauth2Client.getToken(authcode, (err, tokens) => {
          console.log('Received tokens from youtube - saving credentials')
          req.session.authorized = tokens
  
          console.log('Redirecting back to application')
          res.redirect('/list')
      })
  })
  app.use('/*', (req, res, next) => {
      let session = req.session
      if (!session.authorized) {
          // create oauth objects
          let auth = new googleAuth();
          let oauth2Client = new auth.OAuth2(process.env.OAUTH_CLIENT_ID, 
              process.env.OAUTH_CLIENT_SECRET, 
              process.env.OAUTH_REDIRECT_URI)
          
          // generate auth url
          let url = oauth2Client.generateAuthUrl({
              access_type: 'offline',
              scope: SCOPES
          })
  
          // send to auth
          res.redirect(url)
      } else {
          next()
      }
  })
  app.get('/list', (req, res) => {
      // home
      res.render("list")
  })
  app.get('/data(/:filter)?', (req, res) => {
      // load the data
      let filter = req.params.filter
      if (!filter || !filter.trim().length) {
          filter = undefined
      }
      dataprovider.getDays().then(data => {
          if (!filter) {
              // no filter - send as is
              return res.status(200).send(JSON.stringify(data)).end()
          }
          filter = filter.trim().toLowerCase()
          let result = Object.keys(data).reduce((prev, key) => {
              prev[key] = data[key].filter(session => session.title_lowercase.indexOf(filter) >= 0)
              return prev
          }, {})
          res.status(200).send(JSON.stringify(result)).end()
      })
  })
  app.post('/create_playlist', (req, res) => {
      let access_token = req.session.authorized.access_token
      if (!access_token) return res.status(401).end()
      let obj = req.body
      if (!obj || !obj.name || !obj['youtube_ids']) return res.status(417).end()
      
      // create playlist
      youtubeApiRequest('/playlists', 
          access_token, 
          {
              'snippet': {
                  'title': obj.name, 
                  'description': 'Generated by Dreamforce 2019 Playlist Generator'
              },
              'status': {
                  'privacyStatus': 'private'
              }
          }
      ).then(resp => {
          // get generated playlist id
          let playlistId = resp.data.id
  
          // map all youtube video ids into a param object and then 
          // into a promise for adding the video to the playlist
          let videoIds = obj['youtube_ids']
          let b = []
          new Promise((resolve, reject) => {
              let next = () => {
                  let videoId = videoIds.shift()
                  if (!videoId) return resolve()
                  return youtubeApiRequest('/playlistItems', access_token, {
                      'snippet': {
                          'playlistId': playlistId,
                          'resourceId': {
                              'kind': 'youtube#video',
                              'videoId': videoId
                          }
                      }
                  }).then((data) => {
                      b.push(data.data)
                      next()
                  })
              }
              next()
          }).then(() => {
              res.status(201).send(JSON.stringify(b)).end()
          }).catch(err => {
              // one or more additions failed
              return Promise.reject(err)
          })
  
      }).catch(err => {
          // playlist creation or a video additional failed
          console.log(err);
          res.status(500).end()
      })
  })
  
  // listen
  app.listen(process.env.PORT || 8080)
  