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, registerVerifiedUser, 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 address = server.address();
      const port = typeof address === 'object' && address ? address.port : 0;
      server.close((error) => error ? reject(error) : resolve(port));
    });
  });
}

async function request(base, pathname, { method = 'GET', token = '', body, idempotencyKey = '' } = {}) {
  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 (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;
  const response = await fetch(`${base}${pathname}`, {
    method,
    headers,
    body: body == null ? undefined : JSON.stringify(body),
    signal: AbortSignal.timeout(5000)
  });
  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw Object.assign(new Error(payload.message || `${method} ${pathname} failed with ${response.status}`), {
      status: response.status,
      code: payload.code,
      payload
    });
  }
  return payload;
}

async function waitForHealth(base, child, output) {
  for (let attempt = 0; attempt < 100; attempt += 1) {
    if (child.exitCode != null) return false;
    try {
      const response = await fetch(`${base}/api/health`, { signal: AbortSignal.timeout(300) });
      if (response.ok) return true;
    } catch {}
    await new Promise((resolve) => setTimeout(resolve, 80));
  }
  throw new Error(`OnlyCoin HTTP test server did not become healthy.\n${output.join('')}`);
}

test('payment and currency admin APIs drive the public checkout catalog', async (t) => {
  const port = await reservePort();
  const base = `http://127.0.0.1:${port}`;
  const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'onlycoin-payment-http-'));
  const output = [];
  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', 'pipe', 'pipe'],
    windowsHide: true
  });
  child.stdout.on('data', (chunk) => output.push(chunk.toString('utf8')));
  child.stderr.on('data', (chunk) => output.push(chunk.toString('utf8')));

  t.after(async () => {
    if (child.exitCode == null) child.kill('SIGTERM');
    await new Promise((resolve) => {
      if (child.exitCode != null) return resolve();
      const timer = setTimeout(() => {
        if (child.exitCode == null) child.kill('SIGKILL');
        resolve();
      }, 3000);
      child.once('exit', () => { clearTimeout(timer); resolve(); });
    });
    fs.rmSync(dataDir, { recursive: true, force: true });
  });

  await waitForHealth(base, child, output);

  const initialCatalog = await request(base, '/api/public/payments?country=TR');
  assert.deepEqual(initialCatalog.providers, [], 'Unconfigured payment methods must not be advertised publicly.');
  assert.ok(initialCatalog.vipPrices.some((row) => row.currency_code === 'TRY'));
  assert.ok(initialCatalog.vipPrices.some((row) => row.currency_code === 'USD'));

  const adminLogin = await request(base, '/api/admin/login', {
    method: 'POST',
    body: { email: TEST_ADMIN_EMAIL, password: TEST_ADMIN_PASSWORD }
  });
  assert.ok(adminLogin.token);

  const settings = await request(base, '/api/admin/payment-settings', { token: adminLogin.token });
  await request(base, '/api/admin/platform-settings', {
    method: 'PUT', token: adminLogin.token,
    body: { purchases_enabled: true, fiat_payments_enabled: true, bank_transfer_enabled: true }
  });
  await request(base, '/api/admin/payment-providers/bank_transfer', {
    method: 'PUT', token: adminLogin.token, body: { enabled: true }
  });
  await request(base, '/api/admin/currencies/USD', {
    method: 'PUT', token: adminLogin.token,
    body: { enabled: true, rate_to_try_e8: 4000000000, source: 'manual-test' }
  });
  assert.ok(settings.providers.some((row) => row.id === 'paytr'));
  assert.ok(settings.providers.some((row) => row.id === 'iyzico'));
  assert.ok(settings.cryptoAssets.some((row) => row.id === 'btc-mainnet'));
  assert.ok(settings.cryptoAssets.some((row) => row.id === 'usdt-bep20'));

  await request(base, '/api/admin/bank-accounts', {
    method: 'POST',
    token: adminLogin.token,
    body: {
      id: 'bank-try',
      currency_code: 'TRY',
      bank_name: 'OnlyCoin Test Bankası',
      account_holder: 'OnlyCoin Test',
      iban: 'TR000000000000000000000000',
      instructions: 'Sipariş referansını açıklamaya yazın.',
      enabled: true,
      min_amount_minor: 100,
      max_amount_minor: 100000000,
      sort_order: 10
    }
  });

  const catalog = await request(base, '/api/public/payments?country=TR');
  assert.ok(catalog.providers.some((row) => row.id === 'bank_transfer'));
  assert.ok(catalog.bankAccounts.some((row) => row.currency_code === 'TRY'));

  const { verified: userLogin } = await registerVerifiedUser(request, base, {
    email: 'payment-http-user@example.com',
    username: 'PaymentHttpUser',
    displayName: 'Payment HTTP User'
  });

  const checkout = await request(base, '/api/game/payment-checkout', {
    method: 'POST',
    token: userLogin.token,
    idempotencyKey: 'payment-http-bank-vip-0001',
    body: { purpose: 'vip', tier: 1, providerId: 'bank_transfer', currencyCode: 'TRY' }
  });
  assert.equal(checkout.session.status, 'ready');
  assert.equal(checkout.session.instructions.type, 'bank_transfer');
  assert.equal(checkout.session.instructions.bank.currency_code, 'TRY');

  const duplicate = await request(base, '/api/game/payment-checkout', {
    method: 'POST',
    token: userLogin.token,
    idempotencyKey: 'payment-http-bank-vip-0001',
    body: { purpose: 'vip', tier: 1, providerId: 'bank_transfer', currencyCode: 'TRY' }
  });
  assert.equal(duplicate.duplicate, true);

  await request(base, '/api/admin/currencies/EUR', {
    method: 'PUT',
    token: adminLogin.token,
    body: {
      name: 'Euro', symbol: '€', decimals: 2, enabled: true, is_base: false,
      sort_order: 30, rate_to_try_e8: 4660000000, source: 'http-test'
    }
  });
  const afterCurrency = await request(base, '/api/admin/payment-settings', { token: adminLogin.token });
  const euro = afterCurrency.currencies.find((row) => row.code === 'EUR');
  assert.equal(euro.rate_to_try_e8, 4660000000);
  assert.equal(euro.source, 'http-test');
});
