const assert = require('assert');
const {
  BASE1_ALGORITHM_VERSION,
  generateBase1Step,
} = require('../src/lib/base1_algorithm');
const {
  CHAIN_SEED_VERSION,
  deriveNextStepSeedSource,
  genesisStepSeedSource,
  seedSourceCommitment,
} = require('../src/lib/chain_seed');
const { sha256 } = require('../src/lib/hash');
const { verifyOfficialStep } = require('../src/lib/expression_tree_verifier');

function short(hash) {
  return hash ? `${hash.slice(0, 10)}...${hash.slice(-6)}` : '';
}

function solvedResultFromVerification(step, verification, blockHash) {
  return {
    layer_no: step.layer_no,
    step_no: step.step_no,
    solution_hash: verification.answer_hash,
    answer_hash: verification.answer_hash,
    behavior_hash: verification.behavior_hash,
    validation_id: verification.validation_id,
    block_hash: blockHash,
  };
}

function makeBlockHash(height, previousBlockHash, verification) {
  return sha256({
    type: 'sample_block',
    height,
    previous_block_hash: previousBlockHash,
    validation_id: verification.validation_id,
    behavior_hash: verification.behavior_hash,
    answer_hash: verification.answer_hash,
  });
}

function row(label, step, seedSource, verification) {
  return {
    item: label,
    layer: step.layer_no,
    step: step.step_no,
    mode: step.randomness_mode,
    seed_source: short(seedSource.source_hash),
    step_seed: short(step.seed),
    formula_hash: short(step.formula_hash),
    behavior_hash: short(verification.behavior_hash),
    validation_id: short(verification.validation_id),
  };
}

function main() {
  const chainId = 'mazexchain-local-sample';
  const seasonSeed = 'mazex-chain-seed-sample';
  const genesisBlockHash = sha256({ chainId, kind: 'genesis' });

  const seed1 = genesisStepSeedSource({
    chainId,
    seasonSeed,
    algorithm: BASE1_ALGORITHM_VERSION,
    layerNo: 1,
    stepNo: 1,
    genesisHash: genesisBlockHash,
  });
  const step1 = generateBase1Step({ layerNo: 1, stepNo: 1, seedSource: seed1 });
  const verify1 = verifyOfficialStep(step1);
  const block1Hash = makeBlockHash(1, genesisBlockHash, verify1);
  const solved1 = solvedResultFromVerification(step1, verify1, block1Hash);

  const seed2 = deriveNextStepSeedSource({
    chainId,
    seasonSeed,
    algorithm: BASE1_ALGORITHM_VERSION,
    previousStep: step1,
    previousResult: solved1,
    previousBlockHash: block1Hash,
    nextLayerNo: 1,
    nextStepNo: 2,
  });
  const step2 = generateBase1Step({ layerNo: 1, stepNo: 2, seedSource: seed2 });
  const verify2 = verifyOfficialStep(step2);

  const seed2Replay = deriveNextStepSeedSource({
    chainId,
    seasonSeed,
    algorithm: BASE1_ALGORITHM_VERSION,
    previousStep: step1,
    previousResult: solved1,
    previousBlockHash: block1Hash,
    nextLayerNo: 1,
    nextStepNo: 2,
  });
  const step2Replay = generateBase1Step({ layerNo: 1, stepNo: 2, seedSource: seed2Replay });

  const changedSolved1 = {
    ...solved1,
    behavior_hash: sha256({ changed: solved1.behavior_hash }),
  };
  const changedSeed2 = deriveNextStepSeedSource({
    chainId,
    seasonSeed,
    algorithm: BASE1_ALGORITHM_VERSION,
    previousStep: step1,
    previousResult: changedSolved1,
    previousBlockHash: block1Hash,
    nextLayerNo: 1,
    nextStepNo: 2,
  });
  const changedStep2 = generateBase1Step({ layerNo: 1, stepNo: 2, seedSource: changedSeed2 });

  assert.strictEqual(verify1.ok, true);
  assert.strictEqual(verify2.ok, true);
  assert.strictEqual(seed2.source_hash, seed2Replay.source_hash);
  assert.strictEqual(step2.formula_hash, step2Replay.formula_hash);
  assert.notStrictEqual(seed2.source_hash, changedSeed2.source_hash);
  assert.notStrictEqual(step2.formula_hash, changedStep2.formula_hash);

  console.log(`Chain seed version: ${CHAIN_SEED_VERSION}`);
  console.log('\nSeed commitments');
  console.table([
    seedSourceCommitment(seed1),
    seedSourceCommitment(seed2),
    seedSourceCommitment(changedSeed2),
  ].map((item) => ({
    layer: item.layer_no,
    step: item.step_no,
    source_hash: short(item.source_hash),
    step_seed_hash: short(item.step_seed_hash),
    prev_solution: short(item.previous_solution_hash),
    prev_behavior: short(item.previous_behavior_hash),
    prev_block: short(item.previous_block_hash),
  })));

  console.log('\nGenerated steps');
  console.table([
    row('step_1', step1, seed1, verify1),
    row('step_2', step2, seed2, verify2),
    row('step_2_replay', step2Replay, seed2Replay, verify2),
    {
      item: 'step_2_changed_previous_result',
      layer: changedStep2.layer_no,
      step: changedStep2.step_no,
      mode: changedStep2.randomness_mode,
      seed_source: short(changedSeed2.source_hash),
      step_seed: short(changedStep2.seed),
      formula_hash: short(changedStep2.formula_hash),
      behavior_hash: '',
      validation_id: '',
    },
  ]);

  console.log('\nChain seed sample passed: same previous result replays, changed previous result changes the next Step.');
}

main();
