const assert = require('assert');
const fs = require('fs');
const path = require('path');

const ROOT = path.resolve(__dirname, '..');
const sampleDir = path.join(ROOT, 'data', 'crash-safe-storage-sample');
fs.rmSync(sampleDir, { recursive: true, force: true });
fs.mkdirSync(sampleDir, { recursive: true });

process.env.MAZEX_DATA_DIR = sampleDir;
process.env.MAZEX_SHARDED_STORAGE = '1';

const {
  getBase1ChainSummary,
  getCurrentBase1Public,
  loadBase1State,
  makeCandidateFromOfficial,
  resetBase1State,
  submitBase1Candidate,
} = require('../src/lib/base1_state');
const {
  blockSegmentPath,
  loadLatestFromShardedStorage,
  shardedStorageStatus,
} = require('../src/lib/sharded_chain_storage');

function currentCandidate() {
  const state = loadBase1State();
  return makeCandidateFromOfficial(state.officialStepsByKey[state.currentStepKey]);
}

function solveOnce(index) {
  const accepted = submitBase1Candidate({
    solver: `crash-safe-solver-${index}`,
    candidateStep: currentCandidate(),
    expectedHead: getCurrentBase1Public().latest_head_guard,
    chargeFee: true,
  });
  assert.strictEqual(accepted.correct, true);
  return accepted;
}

function main() {
  resetBase1State();
  for (let i = 0; i < 3; i += 1) solveOnce(i + 1);

  const committed = getBase1ChainSummary();
  const state = loadBase1State();
  const lastBlock = state.chain.blocks[state.chain.blocks.length - 1];
  const uncommittedBlock = {
    ...lastBlock,
    height: lastBlock.height + 1,
    previous_block_hash: lastBlock.block_hash,
    block_hash: '0xuncommitted-crash-tail',
  };
  const futureSegment = blockSegmentPath(uncommittedBlock.height);

  fs.appendFileSync(futureSegment, `${JSON.stringify(uncommittedBlock)}\n{"partial_crash_tail":`, 'utf8');

  const hydrated = loadLatestFromShardedStorage({ hydrateBlocks: true });
  assert.strictEqual(hydrated.chain.height, committed.height);
  assert.strictEqual(hydrated.chain.head_hash, committed.head_hash);
  assert.strictEqual(hydrated.chain.blocks.length, committed.height + 1);
  assert(!hydrated.chain.blocks.some((block) => block.block_hash === uncommittedBlock.block_hash));

  console.log(
    JSON.stringify(
      {
        status: 'pass',
        committed_height: committed.height,
        committed_head: committed.head_hash,
        ignored_uncommitted_block: uncommittedBlock.block_hash,
        public_storage_journal: shardedStorageStatus().journal,
      },
      null,
      2,
    ),
  );
}

main();
