const assert = require('assert');
const {
  getCurrentBase1Public,
  loadBase1State,
  makeCandidateFromOfficial,
  resetBase1State,
  submitBase1Candidate,
  verifyBase1Chain,
} = require('../src/lib/base1_state');

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

function makeWrongCandidate(step) {
  const candidate = makeCandidateFromOfficial(step);
  candidate.trees = JSON.parse(JSON.stringify(candidate.trees));
  candidate.trees.u = { type: 'const', value: step.grid_size + 999 };
  return candidate;
}

function currentOfficial(state) {
  return state.officialStepsByKey[state.currentStepKey];
}

function printProgression(label, stateOrPublic) {
  const current = stateOrPublic.current || getCurrentBase1Public(stateOrPublic).current;
  console.log(
    JSON.stringify(
      {
        label,
        layer: current.layer_no,
        step: current.step_no,
        progress: current.progress,
        grid: `${current.grid_size}x${current.grid_size}`,
        seed_source_hash: short(current.seed_source_hash),
        formula_hash: short(current.formula_hash),
        step_commit: short(current.step_commit),
        score: current.score,
      },
      null,
      2,
    ),
  );
}

function main() {
  let state = resetBase1State({
    chainId: 'mazex-base1-progression-sample',
    seasonSeed: 'base1-progression-sample',
  });
  const firstKey = state.currentStepKey;
  const firstStep = currentOfficial(state);
  const firstFormulaHash = firstStep.formula_hash;
  assert.strictEqual(state.generatedStepKeys.length, 1);
  assert.strictEqual(state.currentLayer, 1);
  assert.strictEqual(state.currentStep, 1);
  assert.strictEqual(verifyBase1Chain().ok, true);
  printProgression('before_solve_only_step_1_exists', getCurrentBase1Public(state));

  const wrong = submitBase1Candidate({
    solver: 'wrong-agent',
    candidateStep: makeWrongCandidate(firstStep),
  });
  assert.strictEqual(wrong.correct, false);
  state = loadBase1State();
  assert.strictEqual(state.currentStepKey, firstKey);
  assert.strictEqual(state.generatedStepKeys.length, 1);
  assert.strictEqual(verifyBase1Chain().ok, true);

  const solved = submitBase1Candidate({
    solver: 'base1-agent-alpha',
    candidateStep: makeCandidateFromOfficial(firstStep),
  });
  assert.strictEqual(solved.correct, true);
  assert.strictEqual(solved.verification.stage, 'accepted');
  assert.strictEqual(solved.solve_block.height, 1);
  assert.strictEqual(solved.commit_block.height, 2);
  assert.deepStrictEqual(
    solved.solve_block.transactions.map((tx) => tx.type),
    ['BASE1_SOLVE_STEP', 'BASE1_SUBMIT_FEE_BURNED', 'BASE1_REWARD_MINTED'],
  );
  assert.deepStrictEqual(
    solved.commit_block.transactions.map((tx) => tx.type),
    ['BASE1_STEP_ADVANCED', 'BASE1_STEP_COMMITTED'],
  );

  state = loadBase1State();
  const secondStep = currentOfficial(state);
  assert.strictEqual(state.currentLayer, 1);
  assert.strictEqual(state.currentStep, 2);
  assert.strictEqual(state.generatedStepKeys.length, 2);
  assert.notStrictEqual(secondStep.formula_hash, firstFormulaHash);
  assert.strictEqual(state.solvedSteps.length, 1);
  assert.strictEqual(state.solvedSteps[0].next_seed_source_hash, secondStep.seed_source_hash);
  assert.strictEqual(verifyBase1Chain().ok, true);
  printProgression('after_solve_step_2_committed', getCurrentBase1Public(state));

  console.log('\nBase1 progression chain blocks');
  console.table(
    state.chain.blocks.map((block) => ({
      height: block.height,
      txs: block.transactions.map((tx) => tx.type).join(', '),
      work_score: block.work_score,
      cumulative_work_score: block.cumulative_work_score,
      block_hash: short(block.block_hash),
      state_root: short(block.state_root),
      current_layer: block.state_after.current_layer,
      current_step: block.state_after.current_step,
      current_formula: short(block.state_after.current_formula_hash),
      current_seed: short(block.state_after.current_seed_source_hash),
    })),
  );
  console.log('\nBase1 progression sample passed: Step 2 does not exist before Step 1 is solved, then it is generated from the solve block hash.');
}

main();
