const assert = require('assert');
const {
  getBase1ChainSummary,
  getBase1Leaderboard,
  getBase1RewardLedger,
  getBase1FragmentDownload,
  getBase1FragmentRegistry,
  getBase1FragmentRecord,
  getCurrentBase1Public,
  loadBase1State,
  makeCandidateFromOfficial,
  quarantineBase1Fragment,
  registerBase1Fragment,
  replayBase1Chain,
  resetBase1State,
  sampleBase1Point,
  submitBase1Candidate,
  upgradeBase1Fragment,
  verifyBase1Chain,
} = require('../src/lib/base1_state');

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

function main() {
  resetBase1State();
  const current = getCurrentBase1Public();
  assert.strictEqual(current.current.layer_no, 1);
  assert.strictEqual(current.current.step_no, 1);
  assert(/^0x[0-9a-f]{64}$/.test(current.current.behavior_root));
  assert(/^0x[0-9a-f]{64}$/.test(current.current.behavior_commitment_hash));
  assert(/^0x[0-9a-f]{64}$/.test(current.current.fragment_registry_root));
  assert.strictEqual(current.current.fragment_registry_root, current.latest_head_guard.fragment_registry_root);
  assert(current.fragment_registry.active_count >= 6);
  assert.strictEqual(current.current.behavior_commitment.leaf_count, current.current.verifier.point_count);
  assert.strictEqual(current.fee_schedule.sample_fee, 0);
  assert.strictEqual(current.fee_schedule.invalid_submit_fee, 0);
  assert.strictEqual(verifyBase1Chain().ok, true);

  const beforeFragmentRoot = current.current.fragment_registry_root;
  const registered = registerBase1Fragment({
    author: 'smoke-fragment-author',
    activationDelay: 0,
    fragment: {
      kind: 'operator',
      name: 'smoke.operator.shift',
      version: '1.0.0',
      payload: {
        deterministic: true,
        rule: 'smoke-only deterministic operator fragment',
      },
      budget: {
        min_layer: 1,
        score: 2,
        max_payload_bytes: 4096,
      },
      compatibility: {
        protocol: 'base1',
        min_version: '1.0.0',
      },
    },
  });
  assert.strictEqual(registered.ok, true);
  assert.strictEqual(registered.block.transactions[0].type, 'BASE1_FRAGMENT_REGISTERED');
  assert(/^0x[0-9a-f]{64}$/.test(registered.fragment.fragment_id));
  assert(/^0x[0-9a-f]{64}$/.test(registered.fragment.behavior_fingerprint));
  assert.strictEqual(verifyBase1Chain().ok, true);

  const registryAfterRegister = getBase1FragmentRegistry(undefined, { limit: 100 });
  assert.strictEqual(registryAfterRegister.total_count, current.fragment_registry.total_count + 1);
  assert.notStrictEqual(registryAfterRegister.active_root, beforeFragmentRoot);
  const recordPackage = getBase1FragmentRecord(undefined, { fragmentId: registered.fragment.fragment_id });
  assert.strictEqual(recordPackage.ok, true);
  assert.strictEqual(recordPackage.fragment.fragment_id, registered.fragment.fragment_id);
  assert(Array.isArray(recordPackage.merkle_proof));
  const downloadPackage = getBase1FragmentDownload(undefined, { limit: 100, includePayload: false });
  assert.strictEqual(downloadPackage.ok, true);
  assert(/^0x[0-9a-f]{64}$/.test(downloadPackage.download_hash));
  assert.strictEqual(downloadPackage.records.some((record) => Object.prototype.hasOwnProperty.call(record, 'payload')), false);
  const duplicate = registerBase1Fragment({
    author: 'smoke-fragment-author',
    activationDelay: 0,
    fragment: {
      kind: 'operator',
      name: 'smoke.operator.shift',
      version: '1.0.0',
      payload: {
        deterministic: true,
        rule: 'smoke-only deterministic operator fragment',
      },
      budget: {
        min_layer: 1,
        score: 2,
        max_payload_bytes: 4096,
      },
      compatibility: {
        protocol: 'base1',
        min_version: '1.0.0',
      },
    },
  });
  assert.strictEqual(duplicate.ok, false);
  assert(['FRAGMENT_DUPLICATE', 'FRAGMENT_DUPLICATE_CONTENT', 'FRAGMENT_DUPLICATE_BEHAVIOR'].includes(duplicate.code));
  const upgraded = upgradeBase1Fragment({
    previousFragmentId: registered.fragment.fragment_id,
    author: 'smoke-fragment-author',
    activationDelay: 0,
    fragment: {
      kind: 'operator',
      name: 'smoke.operator.shift',
      version: '1.0.1',
      payload: {
        deterministic: true,
        rule: 'smoke-only upgraded deterministic operator fragment',
        arity: 1,
      },
      budget: {
        min_layer: 1,
        score: 3,
        max_payload_bytes: 4096,
      },
      compatibility: {
        protocol: 'base1',
        min_version: '1.0.0',
      },
    },
  });
  assert.strictEqual(upgraded.ok, true);
  assert.strictEqual(upgraded.block.transactions[0].type, 'BASE1_FRAGMENT_UPGRADED');
  assert.strictEqual(upgraded.previous.retired, true);
  assert.strictEqual(upgraded.fragment.supersedes_fragment_id, registered.fragment.fragment_id);
  assert.strictEqual(verifyBase1Chain().ok, true);
  assert.strictEqual(replayBase1Chain().ok, true);
  const currentAfterRegister = getCurrentBase1Public();
  assert.strictEqual(currentAfterRegister.current.fragment_registry_root, beforeFragmentRoot);

  const quarantined = quarantineBase1Fragment({
    fragmentId: upgraded.fragment.fragment_id,
    challenger: 'smoke-fragment-challenger',
    reasonCode: 'protocol_bug',
    evidence: {
      deterministic: true,
      note: 'smoke test quarantine proof',
      fragment_id: upgraded.fragment.fragment_id,
    },
  });
  assert.strictEqual(quarantined.ok, true);
  assert.strictEqual(quarantined.block.transactions[0].type, 'BASE1_FRAGMENT_QUARANTINED');
  assert(/^0x[0-9a-f]{64}$/.test(quarantined.quarantine.proof_hash));
  assert.strictEqual(verifyBase1Chain().ok, true);
  assert.strictEqual(replayBase1Chain().ok, true);

  const registryAfterQuarantine = getBase1FragmentRegistry(undefined, { limit: 100 });
  assert.strictEqual(registryAfterQuarantine.quarantined_count, 1);
  assert.strictEqual(
    registryAfterQuarantine.records.find((record) => record.fragment_id === upgraded.fragment.fragment_id)?.status,
    'quarantined',
  );
  assert.strictEqual(
    registryAfterQuarantine.records.find((record) => record.fragment_id === registered.fragment.fragment_id)?.status,
    'retired',
  );
  const currentAfterQuarantine = getCurrentBase1Public();
  assert.strictEqual(currentAfterQuarantine.current.fragment_registry_root, beforeFragmentRoot);

  const sample = sampleBase1Point({ userId: 'smoke-solver', x: 7, y: 11 });
  assert(Number.isInteger(sample.u));
  assert(Number.isInteger(sample.v));
  assert.strictEqual(sample.gas_cost, 0);

  const invalid = submitBase1Candidate({
    solver: 'smoke-invalid',
    candidateStep: {
      algorithm: currentAfterQuarantine.current.algorithm,
      trees: {
        u: { type: 'const', value: 1 },
        v: { type: 'const', value: 1 },
      },
    },
    expectedHead: currentAfterQuarantine.latest_head_guard,
  });
  assert.strictEqual(invalid.correct, false);
  assert.strictEqual(invalid.on_chain, false);
  assert.strictEqual(invalid.broadcast, false);

  const accepted = submitBase1Candidate({
    solver: 'smoke-solver',
    candidateStep: currentCandidate(),
    expectedHead: getCurrentBase1Public().latest_head_guard,
    chargeFee: true,
  });
  assert.strictEqual(accepted.correct, true);
  assert.strictEqual(accepted.solver, 'smoke-solver');
  assert(accepted.solve_block.block_hash);
  assert.strictEqual(accepted.solve_block.transactions[0].type, 'BASE1_SOLVE_STEP');
  assert.strictEqual(accepted.solve_block.transactions[0].payload.behavior_root, current.current.behavior_root);
  assert.strictEqual(accepted.solve_block.transactions.some((tx) => tx.type === 'BASE1_SUBMIT_FEE_BURNED'), true);
  assert.strictEqual(accepted.solved.submit_fee_burned, accepted.submit_fee.amount);
  assert.strictEqual(verifyBase1Chain().ok, true);
  assert.strictEqual(replayBase1Chain().ok, true);

  const after = getCurrentBase1Public();
  assert.strictEqual(after.current.step_no, 2);
  const chain = getBase1ChainSummary();
  assert(chain.height >= 2);

  const rewards = getBase1RewardLedger({ limit: 5 });
  assert.strictEqual(rewards[0].recipient, 'smoke-solver');
  assert.strictEqual(rewards[0].amount, accepted.solved.reward_amount);

  const leaderboard = getBase1Leaderboard();
  assert.strictEqual(leaderboard[0].solver, 'smoke-solver');

  console.log('Smoke test passed: free sample -> invalid rejected locally -> valid solve block -> fee burned from reward -> next Step.');
}

main();
