const fs = require('fs')
const crypto = require('crypto')
const got = require('got')
const baseX = require('base-x')
const querystring = require('querystring')

const E = require('./error')
const selfSign = require('./self_sign')

const PORT = 3000
const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'

const bsAlpha = baseX(ALPHA_BASE)

const offsets = {
  73: {
    caHash: 0,
    token: 32,
    hostname: 64,
  },
  /* Future version, with operator ID */
  89: {
    caHash: 0,
    token: 32,
    identifier: 64,
    hostname: 64+16,
  },
}

// [caHash, token, Buffer.from(hostname)]
function extractHostname (totem, oss) {
  return totem.slice(oss.hostname).toString()
}

function extractCaHash (totem, oss) {
  return totem.slice(oss.caHash, oss.token)
}

function extractToken (totem, oss) {
  return totem.slice(oss.token, oss.hostname).toString('hex')
}

function extractIdentifier (totem, oss) {
  return oss.identifier ?
    totem.slice(oss.identifier, oss.hostname).toString('hex') :
    null
}

function pair (totemStr, clientCert, connectionInfoPath, model, numOfCassettes) {
  const totem = Buffer.from(bsAlpha.decode(totemStr))
  const oss = offsets[totem.length]
  if (!oss) throw new Error('Invalid totem length')

  const hostname = extractHostname(totem, oss)
  const expectedCaHash = extractCaHash(totem, oss)
  const token = extractToken(totem, oss)
  const identifier = extractIdentifier(totem, oss)

  const tokenHash = crypto.createHash('sha256').update(token).digest('hex')
  const initialOptions = {
    json: true,
    key: clientCert.key,
    cert: clientCert.cert,
    rejectUnauthorized: false
  }

  const idQueryParam = identifier ? `&id=${identifier}` : ''
  return got(`https://${hostname}:${PORT}/ca?token=${tokenHash}` + idQueryParam, initialOptions)
    .then(r => {
      const ca = r.body.ca
      const caHash = crypto.createHash('sha256').update(ca).digest()

      if (!caHash.equals(expectedCaHash)) throw new E.CaHashError()

      const options = {
        key: clientCert.key,
        cert: clientCert.cert,
        ca
      }

      const query = querystring.stringify({token, model, numOfCassettes})
      return got.post(`https://${hostname}:${PORT}/pair?${query}` + idQueryParam, options)
        .then(() => {
          const connectionInfo = {
            host: hostname,
            ca
          }

          fs.writeFileSync(connectionInfoPath, JSON.stringify(connectionInfo))
        })
    })
   .catch(err => {
     console.log(err)
     throw new Error("Pairing error - Please make sure you have a stable network connection and that you are using the right QR Code")
   })
}

function unpair (connectionInfoPath) {
  fs.unlinkSync(connectionInfoPath)
}

function isPaired (connectionInfoPath) {
  return !!connectionInfo(connectionInfoPath)
}

function connectionInfo (connectionInfoPath) {
  try {
    return JSON.parse(fs.readFileSync(connectionInfoPath))
  } catch (e) {
    return null
  }
}

function init (certPath) {
  return selfSign.generateCertificate()
    .then(cert => {
      fs.writeFileSync(certPath.key, cert.key)
      fs.writeFileSync(certPath.cert, cert.cert)
      return cert
    })
}

function getCert (certPath) {
  try {
    return {
      key: fs.readFileSync(certPath.key),
      cert: fs.readFileSync(certPath.cert)
    }
  } catch (e) {
    return null
  }
}

module.exports = {init, pair, unpair, isPaired, connectionInfo, getCert}

@@ -367,18 +367,8 @@
 = %7B%0A
-  73: %7B%0A  
   ca
@@ -368,34 +368,32 @@
  %7B%0A  caHash: 0,%0A
-  
   token: 32,%0A   
@@ -387,26 +387,24 @@
 oken: 32,%0A  
- 
- 
 hostname: 64
@@ -407,139 +407,8 @@
 : 64
-,%0A  %7D,%0A  /* Future version, with operator ID */%0A  89: %7B%0A    caHash: 0,%0A    token: 32,%0A    identifier: 64,%0A    hostname: 64+16,%0A  %7D,
 %0A%7D%0A%0A
@@ -729,152 +729,8 @@
 %0A%7D%0A%0A
-function extractIdentifier (totem, oss) %7B%0A  return oss.identifier ?%0A    totem.slice(oss.identifier, oss.hostname).toString('hex') :%0A    null%0A%7D%0A%0A
 func
@@ -864,96 +864,8 @@
 tr))
-%0A  const oss = offsets%5Btotem.length%5D%0A  if (!oss) throw new Error('Invalid totem length')
 %0A%0A  
@@ -897,33 +897,37 @@
 ostname(totem, o
-s
+ffset
 s)%0A  const expec
@@ -952,33 +952,37 @@
 tCaHash(totem, o
-s
+ffset
 s)%0A  const token
@@ -1009,60 +1009,13 @@
 m, o
-ss)%0A  const identifier = extractIdentifier(totem, os
+ffset
 s)%0A%0A
@@ -1224,69 +1224,8 @@
  %7D%0A%0A
-  const idQueryParam = identifier ? %60&id=$%7Bidentifier%7D%60 : ''%0A
   re
@@ -1284,31 +1284,16 @@
 enHash%7D%60
- + idQueryParam
 , initia
@@ -1732,23 +1732,8 @@
 ry%7D%60
- + idQueryParam
 , op

revert recent pairing changes