// https://www.sqlitetutorial.net/sqlite-nodejs/
// https://github.com/JoshuaWise/better-sqlite3/blob/HEAD/docs/api.md

module.exports = {
  GetDB,
  AddColumn,
  TableInfo,
  Update,
  Delete,
  CreateTable,
  SelectAll,
  Select,
  Insert,
  CloseDB
}

const Sqlite = require('better-sqlite3')
const logger = require('../utils/logger.js')
const utils = require('../utils/utils.js')

const debugLog = process.env.NS_SQL_DEBUG_LOG

// { asd: 'asd', basd: 4 } => asd = 'asd', basd = 4
function GetSqlQuerry (conditions, type) {
  const res = Object.keys(conditions).reduce((acc, key) => {
    const item = conditions[key]
    if (typeof item === 'string') {
      acc.push(`${key} = '${conditions[key]}'`)
    } else {
      acc.push(`${key} = ${conditions[key]}`)
    }
    return acc
  }, [])
  if (type === 'where') {
    return res.join(' AND ')
  } else {
    return res.join(', ')
  }
}

// -------------------------------------------------------------------------

function GetDB (path) {
  utils.CreatePath(path)
  const res = new Sqlite(path)
  res.pragma('synchronous = OFF')
  return res
}

function DebugLog (msg) {
  if (debugLog) {
    logger.DebugLog(msg, 'sql', 0)
  }
}

function AddColumn (db, table, col) {
  try {
    const colName = Object.keys(col)[0]
    const colType = col.type

    const s = `ALTER TABLE ${table} ADD COLUMN ${colName} ${colType}`
    const stmt = PrepareStatement(db, s)

    return stmt.run()
  } catch (e) {
    console.error(e)
  }
}

function TableInfo (db, table) {
  try {

  } catch (e) {
    console.error(e)
  }
  const s = `PRAGMA table_info(${table})`
  const stmt = PrepareStatement(db, s)

  const infoRes = stmt.all()

  const s2 = `SELECT COUNT(*) FROM ${table}`
  const stmt2 = PrepareStatement(db, s2)

  const countRes = stmt2.get()

  return {
    columns: infoRes,
    dataCount: countRes[Object.keys(countRes)[0]]
  }
}

function Update (db, table, newData, conditions) {
  try {
    const s = `UPDATE ${table} SET ${GetSqlQuerry(newData, 'set')} WHERE ${GetSqlQuerry(conditions, 'where')}`
    const stmt = PrepareStatement(db, s)

    return stmt.run()
  } catch (e) {
    console.error(e)
  }
}

function Delete (db, table, conditions) {
  try {
    const s = `DELETE FROM ${table} WHERE ${GetSqlQuerry(conditions, 'where')}`
    const stmt = PrepareStatement(db, s)

    return stmt.run()
  } catch (e) {
    console.error(e)
  }
}

function CreateTable (db, name, columns, foreignKeys) {
  // CREATE TABLE users(pw text PRIMARY KEY NOT NULL, id number, lastIP text, notes text, loginCount
  // number, lastLogin text, lastAccess text
  //
  // FOREIGN KEY(songartist, songalbum) REFERENCES album(albumartist, albumname) )

  try {
    const cols = Object.keys(columns).reduce((acc, key) => {
      const item = columns[key]
      // FIXME: array, and push stuff, then join()
      const flags = []
      const toCheck = {
        primary: 'PRIMARY KEY',
        notNull: 'NOT NULL',
        unique: 'UNIQUE',
        autoIncrement: 'AUTOINCREMENT',
        defaultZero: 'DEFAULT 0'
      }
      Object.keys(toCheck).forEach((key) => {
        if (item[key]) {
          flags.push(toCheck[key])
        }
      })

      acc.push(`${key} ${item.type} ${flags.join(' ')}`)
      return acc
    }, []).join(', ')

    let fKeys = []
    if (foreignKeys) {
      foreignKeys.forEach((f) => {
        const { keysFrom, table, keysTo } = f
        fKeys.push(`, FOREIGN KEY(${keysFrom.join(', ')}) REFERENCES ${table}(${keysTo.join(', ')})`)
      })
    }

    // IF NOT EXISTS
    const s = `CREATE TABLE ${name}(${cols}${fKeys.join(', ')})`
    const stmt = PrepareStatement(db, s)
    return stmt.run()
  } catch (e) {
    console.error(e)
  }
}

function SelectAll (db, from) {
  try {
    const s = `SELECT * from ${from}`

    const stmt = PrepareStatement(db, s)
    return stmt.all()
  } catch (e) {
    console.error(e)
  }
}

function Select (db, from, conditions) {
  try {
    const s = `SELECT * from ${from} WHERE ${GetSqlQuerry(conditions, 'where')}`

    const stmt = PrepareStatement(db, s)
    return stmt.all()
  } catch (e) {
    console.error(e)
  }
}

function Insert (db, table, data) {
  try {
    const cols = Object.keys(data).reduce((acc, key) => {
      acc.push(`${key}`)
      return acc
    }, []).join(', ')

    const values = Object.keys(data).reduce((acc, key) => {
      const item = data[key]
      if (typeof item === 'string') {
        acc.push(`'${item}'`)
      } else {
        acc.push(`${item}`)
      }
      return acc
    }, []).join(', ')

    const s = `INSERT INTO ${table} (${cols}) VALUES (${values})`
    const stmt = PrepareStatement(db, s)

    return stmt.run()
  } catch (e) {
    console.error(e)
  }
}

function CloseDB (db) {
  db.close((err) => {
    if (err) {
      return console.error(err.message)
    }
    DebugLog('Close the database connection.')
  })
}

// -------------------------------------------------------------------------

function PrepareStatement (db, s) {
  if (!db) {
    throw new Error('DB is undefined in prepare statement! DB action called with undefined db')
  }
  DebugLog(s)
  return db.prepare(s)
}