const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { performance } = require('perf_hooks');

const ROOT = path.resolve(__dirname, '..');
const runId = new Date().toISOString().replace(/[:.]/g, '-');
const defaultDataDir = path.join(ROOT, 'data', `brutal-stress-${runId}`);

process.env.MAZEX_DATA_DIR ||= defaultDataDir;
process.env.MAZEX_AUTO_BROADCAST ||= '0';
process.env.MAZEX_API_RATE_LIMIT ||= '20000';
process.env.MAZEX_API_POST_RATE_LIMIT ||= '20000';
process.env.MAZEX_API_CRITICAL_RATE_LIMIT ||= '10000';
process.env.MAZEX_API_NETWORK_RATE_LIMIT ||= '10000';

const {
  appendBase1ConfirmationBlock,
  createBase1StateSnapshot,
  getBase1Account,
  getBase1ChainSummary,
  getBase1FeeSchedule,
  getBase1RewardLedger,
  getBase1StorageStatus,
  getCurrentBase1Public,
  loadBase1State,
  makeCandidateFromOfficial,
  replayBase1BlocksFromSnapshot,
  replayBase1Chain,
  resetBase1State,
  sampleBase1Point,
  submitBase1Candidate,
  submitBase1Transfer,
  verifyBase1Chain,
} = require('../src/lib/base1_state');
const { evaluateBase1Step, generateBase1Step, layerSchedule } = require('../src/lib/base1_algorithm');
const { blockSegmentPath, loadLatestFromShardedStorage } = require('../src/lib/sharded_chain_storage');
const { createWallet, payloadHash, signPayload, verifySignature } = require('../src/lib/wallet');
const { server } = require('../src/server');

const CONFIG = {
  solves: Math.max(1, Math.trunc(Number(process.env.MAZEX_BRUTAL_SOLVES || 120))),
  samplesPerStep: Math.max(0, Math.trunc(Number(process.env.MAZEX_BRUTAL_SAMPLES_PER_STEP || 8))),
  invalidEvery: Math.max(0, Math.trunc(Number(process.env.MAZEX_BRUTAL_INVALID_EVERY || 3))),
  transfers: Math.max(0, Math.trunc(Number(process.env.MAZEX_BRUTAL_TRANSFERS || 80))),
  wallets: Math.max(1, Math.trunc(Number(process.env.MAZEX_BRUTAL_WALLETS || 1500))),
  highLayerVariants: Math.max(1, Math.trunc(Number(process.env.MAZEX_BRUTAL_HIGH_LAYER_VARIANTS || 20))),
  replayEvery: Math.max(1, Math.trunc(Number(process.env.MAZEX_BRUTAL_REPLAY_EVERY || 12))),
  confirmationsPerSolve: Math.max(0, Math.trunc(Number(process.env.MAZEX_BRUTAL_CONFIRMATIONS_PER_SOLVE || 2))),
  apiBurst: Math.max(1, Math.trunc(Number(process.env.MAZEX_BRUTAL_API_BURST || 120))),
  keepData: ['1', 'true', 'yes'].includes(String(process.env.MAZEX_BRUTAL_KEEP_DATA || '').toLowerCase()),
};

function assertInsideWorkspace(target) {
  const resolved = path.resolve(target);
  const workspace = path.resolve(ROOT);
  assert(resolved.startsWith(workspace + path.sep), `Refusing to touch path outside workspace: ${resolved}`);
  return resolved;
}

function elapsedMs(start) {
  return Number((performance.now() - start).toFixed(2));
}

function memoryMb() {
  return Object.fromEntries(
    Object.entries(process.memoryUsage()).map(([key, value]) => [key, Number((value / 1024 / 1024).toFixed(2))]),
  );
}

function currentOfficialCandidate() {
  const state = loadBase1State();
  const step = state.officialStepsByKey[state.currentStepKey];
  assert(step, 'Current official Step is missing');
  return makeCandidateFromOfficial(step);
}

function latestHeadGuard() {
  return getCurrentBase1Public().latest_head_guard;
}

function solveCurrent({ solver, chargeFee = true, submitFee = null, tag = 'solve' } = {}) {
  const result = submitBase1Candidate({
    solver,
    candidateStep: currentOfficialCandidate(),
    expectedHead: latestHeadGuard(),
    requireExpectedHead: true,
    chargeFee,
    submitFee,
    signedPayloadHash: `${tag}-${Date.now()}-${Math.random()}`,
  });
  assert.strictEqual(result.correct, true, `Solve failed: ${result.code || result.message}`);
  return result;
}

function appendConfirmations(count, label) {
  for (let index = 0; index < count; index += 1) {
    appendBase1ConfirmationBlock({ producer: `${label}-confirm-${index + 1}` });
  }
}

function stressWallets(metrics) {
  const started = performance.now();
  const addresses = new Set();
  const payloadHashes = new Set();
  for (let index = 0; index < CONFIG.wallets; index += 1) {
    const wallet = createWallet();
    assert(!addresses.has(wallet.address), 'Wallet address collision detected');
    addresses.add(wallet.address);
    const payload = {
      type: 'BRUTAL_WALLET_PAYLOAD',
      index,
      nonce: index + 1,
      amount: (index % 97) + 1,
      chain_head_hash: `0xbrutal-${index}`,
    };
    const signed = signPayload({ privateKey: wallet.private_key, payload });
    const verified = verifySignature({
      publicKey: signed.public_key,
      address: signed.address,
      payload: signed.payload,
      signature: signed.signature,
    });
    assert.strictEqual(verified.ok, true, `Wallet signature verification failed at ${index}`);
    assert.strictEqual(signed.payload_hash, payloadHash(payload), 'Payload hash mismatch');
    assert(!payloadHashes.has(signed.payload_hash), 'Payload hash collision detected');
    payloadHashes.add(signed.payload_hash);
  }
  metrics.push({
    module: 'wallet_signature_brutal',
    status: 'pass',
    count: CONFIG.wallets,
    ms: elapsedMs(started),
  });
}

function stressHighLayerFormulaGeneration(metrics) {
  const started = performance.now();
  const layers = [1, 2, 10, 40, 80, 160, 320, 640, 1000, 2000, 5000, 10000, 25000];
  const hashes = new Set();
  let generated = 0;
  let maxScore = 0;
  let maxGrid = 0;
  for (const layerNo of layers) {
    const schedule = layerSchedule(layerNo);
    maxGrid = Math.max(maxGrid, schedule.grid_size);
    for (let variant = 0; variant < CONFIG.highLayerVariants; variant += 1) {
      const stepNo = 1 + ((variant * 13) % schedule.step_count);
      const step = generateBase1Step({
        layerNo,
        stepNo,
        previousHash: `brutal-formula-${layerNo}-${stepNo}-${variant}-${Math.random()}`,
      });
      assert(!hashes.has(step.formula_hash), 'Formula hash collision detected');
      hashes.add(step.formula_hash);
      generated += 1;
      maxScore = Math.max(maxScore, step.scores.final_score);
      const N = step.grid_size;
      const probes = [
        [0, 0],
        [1 % N, 2 % N],
        [Math.floor(N / 2), Math.floor(N / 3)],
        [N - 1, N - 1],
      ];
      for (const [x, y] of probes) {
        const output = evaluateBase1Step(step, x, y, {
          click_count: variant + 1,
          last_x: (variant * 17) % N,
          last_y: (variant * 23) % N,
          elapsed_seconds: layerNo * 7 + variant,
          chain_state: `brutal-chain-state-${layerNo}-${variant}`,
        });
        assert(Number.isInteger(output.u) && output.u >= 0 && output.u < N, 'u is out of grid');
        assert(Number.isInteger(output.v) && output.v >= 0 && output.v < N, 'v is out of grid');
      }
    }
  }
  metrics.push({
    module: 'high_layer_formula_generation',
    status: 'pass',
    generated,
    unique_hashes: hashes.size,
    max_score: maxScore,
    max_grid: maxGrid,
    ms: elapsedMs(started),
  });
}

function stressChainState(metrics) {
  const started = performance.now();
  const solver = 'brutal-solver';
  const fees = getBase1FeeSchedule();
  let samples = 0;
  let invalidSubmissions = 0;
  let paidSolves = 0;
  let submitFeesBurned = 0;
  let replayMaxMs = 0;
  const seenStepCommits = new Set();

  resetBase1State();
  const bootstrap = solveCurrent({ solver, chargeFee: false, tag: 'bootstrap' });
  seenStepCommits.add(bootstrap.solved.step_commit);
  appendConfirmations(3, 'bootstrap');

  for (let index = 1; index <= CONFIG.solves; index += 1) {
    const current = getCurrentBase1Public().current;
    assert(current, 'Current public step missing');
    assert(!seenStepCommits.has(current.step_commit), 'Step commitment repeated during brutal stress run');

    for (let sampleIndex = 0; sampleIndex < CONFIG.samplesPerStep; sampleIndex += 1) {
      const x = (index * 31 + sampleIndex * 7) % current.grid_size;
      const y = (index * 37 + sampleIndex * 11) % current.grid_size;
      const sample = sampleBase1Point({ userId: solver, x, y });
      assert(sample.u >= 0 && sample.u < current.grid_size, 'Sample u out of grid');
      assert(sample.v >= 0 && sample.v < current.grid_size, 'Sample v out of grid');
      assert.strictEqual(sample.gas_cost, fees.sample_fee, 'Sampling must remain free');
      samples += 1;
    }

    if (CONFIG.invalidEvery > 0 && index % CONFIG.invalidEvery === 0) {
      const beforeHead = getBase1ChainSummary();
      const rejected = submitBase1Candidate({
        solver,
        candidateStep: { algorithm: 'brutal-invalid-candidate', trees: { u: { type: 'const', value: -1 } } },
        expectedHead: latestHeadGuard(),
        requireExpectedHead: true,
        chargeFee: true,
        submitFee: current.invalid_submit_cost,
        signedPayloadHash: `brutal-invalid-${index}`,
      });
      const afterHead = getBase1ChainSummary();
      assert.strictEqual(rejected.correct, false, 'Invalid candidate unexpectedly passed');
      assert.strictEqual(rejected.on_chain, false, 'Invalid candidate entered chain');
      assert.strictEqual(rejected.submit_fee, null, 'Invalid candidate burned fee');
      assert.strictEqual(afterHead.head_hash, beforeHead.head_hash, 'Invalid candidate changed chain head');
      invalidSubmissions += 1;
    }

    const submitFee = getCurrentBase1Public().current.submit_cost;
    const solved = solveCurrent({ solver, chargeFee: true, submitFee, tag: `paid-${index}` });
    assert.strictEqual(solved.submit_fee?.amount, submitFee, 'Submit fee mismatch');
    submitFeesBurned += submitFee;
    paidSolves += 1;
    seenStepCommits.add(current.step_commit);
    appendConfirmations(CONFIG.confirmationsPerSolve, `paid-${index}`);

    if (index % CONFIG.replayEvery === 0) {
      const replayStarted = performance.now();
      const replay = replayBase1Chain();
      const replayMs = elapsedMs(replayStarted);
      replayMaxMs = Math.max(replayMaxMs, replayMs);
      assert.strictEqual(replay.ok, true, `Replay failed at solve ${index}: ${replay.code || replay.message}`);
    }
  }

  let transfers = 0;
  for (let index = 1; index <= CONFIG.transfers; index += 1) {
    const account = getBase1Account(solver);
    const amount = 1 + (index % 11);
    if (account.confirmed_balance < amount + fees.transfer_fee) break;
    const transfer = submitBase1Transfer({
      from: solver,
      to: `brutal-receiver-${index}`,
      amount,
      fee: fees.transfer_fee,
      nonce: account.nonce + 1,
      expectedHead: latestHeadGuard(),
      signedPayloadHash: `brutal-transfer-${index}`,
    });
    assert.strictEqual(transfer.ok, true, `Transfer failed: ${transfer.code || transfer.message}`);
    transfers += 1;
  }

  const replayStarted = performance.now();
  const replay = replayBase1Chain();
  const finalReplayMs = elapsedMs(replayStarted);
  replayMaxMs = Math.max(replayMaxMs, finalReplayMs);
  assert.strictEqual(replay.ok, true, `Final replay failed: ${replay.code || replay.message}`);

  const state = loadBase1State();
  const snapshotAnchorIndex = Math.max(0, Math.floor((state.chain.blocks.length - 1) * 0.75));
  const snapshot = createBase1StateSnapshot({
    state,
    height: state.chain.blocks[snapshotAnchorIndex].height,
  });
  const snapshotReplayStarted = performance.now();
  const snapshotReplay = replayBase1BlocksFromSnapshot({
    snapshot,
    blocks: state.chain.blocks.slice(snapshotAnchorIndex + 1),
  });
  const snapshotReplayMs = elapsedMs(snapshotReplayStarted);
  assert.strictEqual(snapshotReplay.ok, true, `Snapshot replay failed: ${snapshotReplay.code || snapshotReplay.message}`);
  assert.strictEqual(snapshotReplay.chain.head_hash, replay.chain.head_hash, 'Snapshot replay head mismatch');

  const verify = verifyBase1Chain();
  assert.strictEqual(verify.ok, true, `Chain verification failed: ${verify.code || verify.message}`);
  const expectedBurned = submitFeesBurned + transfers * fees.transfer_fee;
  assert.strictEqual(state.burnedFees, expectedBurned, 'Burned fee accounting mismatch');
  assert(getBase1RewardLedger({ limit: 100000 }).length >= paidSolves, 'Reward ledger missing solve rewards');

  const storage = getBase1StorageStatus();
  metrics.push({
    module: 'chain_state_brutal',
    status: 'pass',
    paid_solves: paidSolves,
    invalid_submissions: invalidSubmissions,
    samples,
    transfers,
    height: getBase1ChainSummary().height,
    cumulative_work: getBase1ChainSummary().cumulative_work_score,
    burned_fees: state.burnedFees,
    storage_bytes: storage.bytes.total,
    block_files: storage.block_files,
    replay_max_ms: replayMaxMs,
    final_replay_ms: finalReplayMs,
    snapshot_anchor_height: snapshot.anchor.height,
    snapshot_replayed_blocks: snapshotReplay.checked_blocks,
    snapshot_replay_ms: snapshotReplayMs,
    ms: elapsedMs(started),
  });
}

function stressCrashSafeTail(metrics) {
  const started = performance.now();
  const before = getBase1ChainSummary();
  const state = loadBase1State();
  const lastBlock = state.chain.blocks[state.chain.blocks.length - 1];
  const fakeTail = {
    ...lastBlock,
    height: lastBlock.height + 1,
    previous_block_hash: lastBlock.block_hash,
    block_hash: '0xbrutal-uncommitted-tail',
  };
  fs.appendFileSync(blockSegmentPath(fakeTail.height), `${JSON.stringify(fakeTail)}\n{"partial":`, 'utf8');
  const hydrated = loadLatestFromShardedStorage({ hydrateBlocks: true });
  assert.strictEqual(hydrated.chain.height, before.height, 'Crash tail changed hydrated height');
  assert.strictEqual(hydrated.chain.head_hash, before.head_hash, 'Crash tail changed hydrated head');
  assert(!hydrated.chain.blocks.some((block) => block.block_hash === fakeTail.block_hash), 'Uncommitted tail was loaded');
  metrics.push({
    module: 'crash_safe_tail',
    status: 'pass',
    committed_height: before.height,
    ignored_tail: fakeTail.block_hash,
    ms: elapsedMs(started),
  });
}

async function request(baseUrl, pathName, options = {}) {
  const response = await fetch(`${baseUrl}${pathName}`, options);
  const text = await response.text();
  let body = {};
  try {
    body = text ? JSON.parse(text) : {};
  } catch {
    body = { raw: text };
  }
  return { response, body };
}

async function stressApiSecurity(metrics) {
  const started = performance.now();
  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
  const { port } = server.address();
  const baseUrl = `http://127.0.0.1:${port}`;
  try {
    const healthChecks = [];
    for (let index = 0; index < CONFIG.apiBurst; index += 1) {
      healthChecks.push(request(baseUrl, '/api/health'));
    }
    const healthResponses = await Promise.all(healthChecks);
    assert(healthResponses.every((item) => item.response.status === 200), 'Health burst failed');
    assert(
      healthResponses.every((item) => item.response.headers.get('x-request-id')),
      'Health burst missing request ids',
    );

    const badType = await request(baseUrl, '/api/base1/sample', {
      method: 'POST',
      headers: { 'Content-Type': 'text/plain' },
      body: '{"x":1,"y":1}',
    });
    assert.strictEqual(badType.response.status, 415, 'Content-Type gate failed');

    const badJson = await request(baseUrl, '/api/base1/sample', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: '{"x":1,"__proto__":{"polluted":true}}',
    });
    assert.strictEqual(badJson.response.status, 400, 'Prototype pollution gate failed');
    assert.strictEqual(badJson.body.code, 'JSON_FORBIDDEN_KEY', 'Unexpected forbidden-key error code');

    const badOrigin = await request(baseUrl, '/api/base1/sample', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Origin: 'http://evil.example' },
      body: '{"x":1,"y":1}',
    });
    assert.strictEqual(badOrigin.response.status, 403, 'Origin gate failed');

    const summary = await request(baseUrl, '/api/base1/chain/summary');
    assert.strictEqual(summary.response.status, 200, 'Chain summary failed after API stress');

    metrics.push({
      module: 'api_security_brutal',
      status: 'pass',
      health_burst: CONFIG.apiBurst,
      bad_type_status: badType.response.status,
      forbidden_key_status: badJson.response.status,
      bad_origin_status: badOrigin.response.status,
      height: summary.body.height,
      ms: elapsedMs(started),
    });
  } finally {
    await new Promise((resolve) => server.close(resolve));
  }
}

async function main() {
  const dataDir = assertInsideWorkspace(process.env.MAZEX_DATA_DIR);
  fs.rmSync(dataDir, { recursive: true, force: true });
  fs.mkdirSync(dataDir, { recursive: true });

  const metrics = [];
  const started = performance.now();
  let report;
  try {
    stressWallets(metrics);
    stressHighLayerFormulaGeneration(metrics);
    stressChainState(metrics);
    stressCrashSafeTail(metrics);
    await stressApiSecurity(metrics);

    report = {
      ok: true,
      run_id: runId,
      config: CONFIG,
      data_dir: dataDir,
      total_ms: elapsedMs(started),
      memory_mb: memoryMb(),
      chain: getBase1ChainSummary(),
      storage: getBase1StorageStatus(),
      metrics,
    };
    console.log(JSON.stringify(report, null, 2));
    console.table(metrics);
  } catch (error) {
    report = {
      ok: false,
      run_id: runId,
      config: CONFIG,
      data_dir: dataDir,
      total_ms: elapsedMs(started),
      memory_mb: memoryMb(),
      error: {
        message: error.message,
        stack: error.stack,
      },
      metrics,
    };
    console.error(JSON.stringify(report, null, 2));
    process.exitCode = 1;
  } finally {
    if (CONFIG.keepData) {
      fs.writeFileSync(path.join(dataDir, 'brutal_stress_result.json'), JSON.stringify(report, null, 2), 'utf8');
    } else if (fs.existsSync(dataDir)) {
      fs.rmSync(dataDir, { recursive: true, force: true });
    }
  }
}

main();
