// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-brown; icon-glyph: power-off;
// Version 1.0.0

//////////////////////////////////////////////////////
let fm
try {
  fm = FileManager.iCloud()
  fm.documentsDirectory()
} catch (e) {
  console.log(e)
  fm = FileManager.local()
}

const settingsPath = fm.joinPath(fm.documentsDirectory(), 'health-check-settings.json')
if (!fm.fileExists(settingsPath)) {
  const examplePath = fm.joinPath(fm.documentsDirectory(), 'health-check-settings.example.json')
  fm.writeString(examplePath, JSON.stringify([{
    name: 'Server 1',
    endpoint: 'https://example.com/api/health',
    expectedContentType: 'application/json',
    timeoutInterval: 1,
    notification: true,
    headers: [{key: 'x-customer-header-key',value: 'x-customer-header-value'}]
  }], null, 2))
  
  throw new Error('No health-check-settings.json found! Please rename health-check-settings.example.json to health-check-settings.json and add your settings.')
} else {
  try {
    fm.downloadFileFromiCloud(settingsPath)
  } catch(e) {
    console.error(e)
  } 
}
//////////////////////////////////////////////////////
const chooseRandom = (arr, num = 1) => {
   const res = [];
   for(let i = 0; i < num; ){
      const random = Math.floor(Math.random() * arr.length);
      if(res.indexOf(arr[random]) !== -1){
         continue;
      };
      res.push(arr[random]);
      i++;
   };
   return res;
};
//////////////////////////////////////////////////////
const check = async ({ endpoint, headers, timeoutInterval }) => {
  const req = new Request(endpoint)
  req.timeoutInterval = timeoutInterval || 5
  if (headers) {
    let i
    const h = {}
    for (i = 0; i < headers.length; i++) {
      h[headers[i].key] = headers[i].value
    }
    req.headers = h
  }
  
  let body, httpStatus, contentType
  try {
    const res = await req.loadString()
    if (req.response.headers['Content-Type'] === 'application/json') {
      body = JSON.parse(res)
    }
    httpStatus = req.response.statusCode
    contentType = req.response.headers['Content-Type']
  } catch (e) {
    httpStatus = 504
  }
  return {
    httpStatus,
    contentType,
    body
  }
}
//////////////////////////////////////////////////////
const settings = JSON.parse(fm.readString(settingsPath))

let i
for (i = 0; i < settings.length; i++) {
  const { httpStatus, contentType } = await check(settings[i])
  if (settings[i].history === undefined) {
    settings[i].history = []
  }
  
  let status = 'healthy'
  if (!/^2/.test(httpStatus)) {
    status = 'unhealthy'
  }
  if (settings[i].contentType && settings[i].expectedContentType !== contentType) {
    status = 'unhealthy'
  }
  settings[i].history.push({
    date: new Date().toISOString(),
    status
  })
  settings[i].history = settings[i].history.splice(-40)
  if (settings[i].notification && /^2/.test(httpStatus) === false) {
    const n = new Notification()
    n.body = `🚨 Service '${settings[i].name}' is ${(httpStatus === 504) ? 'slow' : 'unhealthy'}`
    n.schedule()
  }
}
//////////////////////////////////////////////////////
fm.writeString(settingsPath, JSON.stringify(settings, null, 2))
//////////////////////////////////////////////////////

const widget = new ListWidget()

const titleStack = widget.addStack()
titleStack.layoutHorizontally()

const widgetTitle = titleStack.addText('Health Check')

titleStack.addSpacer()

const globalStatus = settings.filter((e) => {
  return e.history[e.history.length - 1].status === 'healthy'
})

const widgetGlobalStatus = titleStack.addText(`${globalStatus.length}/${settings.length} healthy`)
widgetGlobalStatus.font = Font.regularSystemFont(10)

widget.addSpacer(5)

const stack = widget.addStack()
stack.layoutHorizontally()

const leftColumn = stack.addStack()
leftColumn.layoutVertically()
leftColumn.spacing = 5

let rightColumn
if (config.widgetFamily !== 'small' ) {
  stack.addSpacer(20)
  rightColumn = stack.addStack()
  rightColumn.layoutVertically()
  rightColumn.spacing = 5
}

const maxView = (config.widgetFamily !== 'large') ? 4 : 8
let toShow

if (settings.length > maxView) {
  toShow = chooseRandom(settings, maxView)
} else {
  toShow = settings
}

for (i = 0; i < toShow.length; i++) {
  const lastCheck = toShow[i].history[toShow[i].history.length - 1]
  const labelText = leftColumn.addText(`${lastCheck.status === 'healthy' ? '🟢' : '⚠️'} ${toShow[i].name}`)
  labelText.font = Font.regularSystemFont(12)
  labelText.lineLimit = 1
  
  
  if (config.widgetFamily !== 'small' ) {
    let j
    const field = rightColumn.addStack()
    field.layoutHorizontally()
    for (j = 0; j < toShow[i].history.length; j++) {
      const status = field.addText('|')
      status.font = Font.regularSystemFont(12)
      status.textColor = (toShow[i].history[j].status === 'healthy') ? Color.green() : Color.red()
    }
  }
}
widget.addSpacer()

const widgetUpdate = widget.addText(`Last Check: ${new Date().toLocaleString()}`)
widgetUpdate.font = Font.regularSystemFont(8)
widgetUpdate.textColor = Color.lightGray()
widgetUpdate.centerAlignText()

if (!config.runsInWidget) {
  await widget.presentLarge()
} else {
  // Tell the system to show the widget.
  Script.setWidget(widget)
  Script.complete()
}