'use strict';

const bcoin = require('../');
const assert = require('assert');
const BufferWriter = require('../lib/utils/writer');
let file = process.argv[2];

assert(typeof file === 'string', 'Please pass in a database path.');

file = file.replace(/\.ldb\/?$/, '');

const db = bcoin.ldb({
  location: file,
  db: 'leveldb',
  compression: true,
  cacheSize: 32 << 20,
  createIfMissing: false,
  bufferKeys: true
});

function makeKey(data) {
  const height = data.readUInt32LE(1, true);
  const key = Buffer.allocUnsafe(5);
  key[0] = 0x48;
  key.writeUInt32BE(height, 1, true);
  return key;
}

async function checkVersion() {
  console.log('Checking version.');

  const data = await db.get('V');

  if (!data)
    return;

  const ver = data.readUInt32LE(0, true);

  if (ver !== 0)
    throw Error(`DB is version ${ver}.`);
}

async function updateState() {
  console.log('Updating chain state.');

  const data = await db.get('R');

  if (!data || data.length < 32)
    throw new Error('No chain state.');

  const hash = data.slice(0, 32);

  let p = new BufferWriter();
  p.writeHash(hash);
  p.writeU64(0);
  p.writeU64(0);
  p.writeU64(0);
  p = p.render();

  const batch = db.batch();

  batch.put('R', p);

  const ver = Buffer.allocUnsafe(4);
  ver.writeUInt32LE(1, 0, true);
  batch.put('V', ver);

  await batch.write();

  console.log('Updated chain state.');
}

async function updateEndian() {
  const batch = db.batch();
  let total = 0;

  console.log('Updating endianness.');
  console.log('Iterating...');

  const iter = db.iterator({
    gte: Buffer.from('4800000000', 'hex'),
    lte: Buffer.from('48ffffffff', 'hex'),
    values: true
  });

  for (;;) {
    const item = await iter.next();

    if (!item)
      break;

    batch.del(item.key);
    batch.put(makeKey(item.key), item.value);
    total++;
  }

  console.log('Migrating %d items.', total);

  await batch.write();

  console.log('Migrated endianness.');
}

(async () => {
  await db.open();
  console.log('Opened %s.', file);
  await checkVersion();
  await updateState();
  await updateEndian();
})().then(() => {
  console.log('Migration complete.');
  process.exit(0);
});
