const fs = require('fs');
const {
  BASE1_DATA_PATH,
  getBase1Account,
  getBase1ChainSummary,
  getCurrentBase1Public,
  loadBase1State,
  makeCandidateFromOfficial,
  receiveBase1SolveCandidatePackage,
  resetBase1State,
  submitBase1Candidate,
  verifyBase1Chain,
} = require('../src/lib/base1_state');

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

function wrongCandidate() {
  const candidate = candidateForCurrentStep();
  candidate.trees = JSON.parse(JSON.stringify(candidate.trees));
  candidate.trees.u = { type: 'const', value: 999999 };
  return candidate;
}

function main() {
  const hadState = fs.existsSync(BASE1_DATA_PATH);
  const backup = hadState ? fs.readFileSync(BASE1_DATA_PATH, 'utf8') : null;

  try {
    resetBase1State();
    const solver = 'network-safety-solver';
    const beforeHead = getBase1ChainSummary();
    const invalid = submitBase1Candidate({
      solver,
      candidateStep: wrongCandidate(),
      expectedHead: getCurrentBase1Public().latest_head_guard,
      requireExpectedHead: true,
      chargeFee: true,
      submitFee: getCurrentBase1Public().current.submit_cost,
      signedPayloadHash: 'network-safety-invalid',
    });
    if (invalid.correct) throw new Error('Invalid candidate unexpectedly passed');
    if (invalid.on_chain !== false || invalid.submit_fee !== null) {
      throw new Error('Invalid candidate must stay off-chain without fee burn');
    }
    const afterInvalidHead = getBase1ChainSummary();
    const accountAfterInvalid = getBase1Account(solver);
    if (afterInvalidHead.head_hash !== beforeHead.head_hash || accountAfterInvalid.nonce !== 0) {
      throw new Error('Invalid candidate changed chain head or account nonce');
    }

    const oversized = receiveBase1SolveCandidatePackage({
      solver,
      candidateStep: {
        ...candidateForCurrentStep(),
        oversized_note: 'x'.repeat(300 * 1024),
      },
      expectedHead: getCurrentBase1Public().latest_head_guard,
      source: 'oversized-peer',
    });
    if (oversized.code !== 'CANDIDATE_PACKAGE_TOO_LARGE') {
      throw new Error(`Expected oversized candidate rejection, got ${oversized.code || oversized.message}`);
    }

    const valid = receiveBase1SolveCandidatePackage({
      solver,
      candidateStep: candidateForCurrentStep(),
      expectedHead: getCurrentBase1Public().latest_head_guard,
      source: 'valid-peer',
    });
    if (!valid.ok || !valid.accepted || valid.duplicate) {
      throw new Error(`Expected first valid candidate confirmation, got ${valid.code || valid.message}`);
    }

    const duplicate = receiveBase1SolveCandidatePackage({
      solver,
      candidateStep: candidateForCurrentStep(),
      expectedHead: getCurrentBase1Public().latest_head_guard,
      answerHash: valid.confirmation.answer_hash,
      source: 'duplicate-peer',
    });
    if (!duplicate.ok || !duplicate.duplicate || duplicate.code !== 'DUPLICATE_ANSWER_HASH_DROPPED') {
      throw new Error(`Expected duplicate answer_hash drop, got ${duplicate.code || duplicate.message}`);
    }

    const chain = verifyBase1Chain();
    if (!chain.ok) throw new Error(`Chain verification failed: ${chain.code || chain.message}`);

    console.table([
      {
        check: 'invalid candidate',
        status: 'off-chain',
        fee: invalid.submit_fee,
        head_unchanged: afterInvalidHead.head_hash === beforeHead.head_hash,
      },
      {
        check: 'oversized package',
        status: oversized.code,
        bytes: oversized.package_bytes,
      },
      {
        check: 'valid package',
        status: valid.code,
        witness: valid.confirmation.witness_hash.slice(0, 16),
      },
      {
        check: 'duplicate answer_hash',
        status: duplicate.code,
        duplicate: duplicate.duplicate,
      },
    ]);
  } finally {
    if (hadState) {
      fs.writeFileSync(BASE1_DATA_PATH, backup, 'utf8');
    } else if (fs.existsSync(BASE1_DATA_PATH)) {
      fs.unlinkSync(BASE1_DATA_PATH);
    }
  }
}

main();
