import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import net from 'node:net';
import os from 'node:os';
import path from 'node:path';
import { spawn } from 'node:child_process';
import { prepareTestRuntime, serverTestEnv, TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD } from './helpers.js';

function reservePort() {
  return new Promise((resolve, reject) => {
    const server = net.createServer();
    server.once('error', reject);
    server.listen(0, '127.0.0.1', () => {
      const port = server.address().port;
      server.close((error) => error ? reject(error) : resolve(port));
    });
  });
}

async function call(base, pathname, { method = 'GET', token = '', body, key = '' } = {}) {
  const headers = { Accept: 'application/json', 'X-Request-ID': crypto.randomUUID() };
  if (token) headers.Authorization = `Bearer ${token}`;
  if (body != null) headers['Content-Type'] = 'application/json';
  if (key) headers['Idempotency-Key'] = key;
  const response = await fetch(base + pathname, { method, headers, body: body == null ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(15000) });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) throw Object.assign(new Error(payload.message || `${pathname}: ${response.status}`), { status: response.status, code: payload.code, payload });
  return payload;
}

async function healthy(base, child) {
  for (let i = 0; i < 120; i += 1) {
    if (child.exitCode != null) return false;
    try { if ((await fetch(base + '/api/health')).ok) return true; } catch {}
    await new Promise((resolve) => setTimeout(resolve, 75));
  }
  return false;
}

test('contract absent uses manual distribution with wallet refresh and idempotent tx tracking', async (t) => {
  const port = await reservePort();
  const base = `http://127.0.0.1:${port}`;
  const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'onlycoin-manual-distribution-'));
  const adminPassword = TEST_ADMIN_PASSWORD;
  const rootDir = path.resolve(import.meta.dirname, '../..');
  prepareTestRuntime(rootDir, dataDir, { APP_ORIGIN: base });
  const child = spawn(process.execPath, ['--no-warnings', 'server/src/main.js'], {
    cwd: rootDir,
    env: serverTestEnv({ dataDir, port, base }),
    stdio: ['ignore', 'ignore', 'pipe'], windowsHide: true
  });
  t.after(async () => {
    if (child.exitCode == null) child.kill('SIGTERM');
    await new Promise((resolve) => child.exitCode != null ? resolve() : child.once('exit', resolve));
    fs.rmSync(dataDir, { recursive: true, force: true });
  });
  assert.equal(await healthy(base, child), true);

  const admin = await call(base, '/api/admin/login', { method: 'POST', body: { email: TEST_ADMIN_EMAIL, password: adminPassword } });
  await call(base, '/api/admin/platform-settings', { method: 'PUT', token: admin.token, body: {
    email_verification_required: true,
    presale_enabled: true,
    wallet_address_cooldown_hours: 0,
    project_token_contract: '',
    distribution_contract_address: '',
    distribution_mode: 'auto_contract',
    distribution_enabled: true,
    distribution_confirmations_required: 1
  } });
  const registered = await call(base, '/api/auth/register', { method: 'POST', body: { email: 'manual@example.com', username: 'ManualUser', displayName: 'Manual User', password: 'ManualUser2026A', confirmPassword: 'ManualUser2026A' } });
  const verified = await call(base, '/api/auth/email/verify', { method: 'POST', body: { email: 'manual@example.com', code: registered.debugVerificationCode } });
  const userToken = verified.token;
  assert.ok(userToken);

  const cfg = (await call(base, '/api/admin/presale-allocation', { token: admin.token })).config;
  await call(base, '/api/admin/presale-allocation/settings', { method: 'PUT', token: admin.token, body: {
    policy: 'launch_price', totalSupplyTokenBase: cfg.totalSupplyTokenBase, poolTokenBase: cfg.poolTokenBase,
    referencePriceUsdtMicros: 12500, launchPriceUsdtMicros: 30000, termsVersion: '1.0', autoFinalizeAtLaunch: false
  } });
  const order = await call(base, '/api/game/presale/order', { method: 'POST', token: userToken, key: 'manual-presale-order-0001', body: { amountUsdtMicros: 10_000_000 } });
  await call(base, `/api/admin/payment-orders/${order.order.id}/approve`, { method: 'POST', token: admin.token, body: {} });
  const snapshot = await call(base, '/api/admin/presale-allocation/snapshots', { method: 'POST', token: admin.token, body: {} });
  await call(base, `/api/admin/presale-allocation/snapshots/${snapshot.snapshot.id}/approve`, { method: 'POST', token: admin.token, body: { note: 'manual test' } });
  await call(base, `/api/admin/presale-allocation/snapshots/${snapshot.snapshot.id}/finalize`, { method: 'POST', token: admin.token, body: {} });

  const campaign = await call(base, '/api/admin/distributions', { method: 'POST', token: admin.token, body: { name: 'Manual Mainnet Distribution', mode: 'auto_contract' } });
  assert.equal(campaign.campaign.mode, 'manual_withdrawal');
  assert.equal(campaign.items[0].status, 'waiting_wallet');

  const wallet = await call(base, '/api/account/wallets/mainnet', { method: 'POST', token: userToken, body: {
    address: '0x3333333333333333333333333333333333333333', confirmAddress: '0x3333333333333333333333333333333333333333', network: 'BSC Mainnet', chainId: 56, label: 'Manual Wallet'
  } });
  await call(base, `/api/account/wallets/${wallet.wallet.id}/confirm`, { method: 'POST', token: userToken, body: { code: wallet.debugConfirmationCode } });
  const refreshed = await call(base, `/api/admin/distributions/${campaign.campaign.id}/refresh-wallets`, { method: 'POST', token: admin.token, body: {} });
  assert.equal(refreshed.updated, 1);
  const overview = await call(base, '/api/admin/distributions', { token: admin.token });
  const item = overview.recentItems.find((row) => row.campaign_id === campaign.campaign.id);
  assert.equal(item.status, 'manual');

  const txHash = `0x${'a'.repeat(64)}`;
  const broadcast = await call(base, `/api/admin/distribution-items/${item.id}/broadcast`, { method: 'POST', token: admin.token, body: { txHash } });
  assert.equal(broadcast.item.status, 'broadcast');
  const duplicate = await call(base, `/api/admin/distribution-items/${item.id}/broadcast`, { method: 'POST', token: admin.token, body: { txHash } });
  assert.equal(duplicate.duplicate, true);
  const confirmed = await call(base, `/api/admin/distribution-items/${item.id}/confirm`, { method: 'POST', token: admin.token, body: {
    txHash, chainId: 56, contract: '', recipient: item.address, amountBase: item.amount_token_base, confirmations: 1, blockNumber: '999'
  } });
  assert.equal(confirmed.item.status, 'confirmed');
  const finalOverview = await call(base, '/api/admin/distributions', { token: admin.token });
  assert.equal(finalOverview.campaigns.find((row) => row.id === campaign.campaign.id).status, 'completed');
});
