// End-to-end smoke test for the Hotmart webhook (ECO-5).
//
//   node scripts/test-hotmart-webhook.mjs
//
// Runs in test mode (whatsappAdapter buffers messages, group create
// returns synthetic JID). Verifies:
//   1. Missing HOTMART_HOTTOK env → 503
//   2. Bad hottok in payload → 401
//   3. APPROVED payload → mentee dir created, accessLevel=course_only,
//      paymentStatus=approved, accessExpiresAt set ~90d out, group created,
//      welcome message buffered.
//   4. Replay of same APPROVED payload → 200 duplicate (no double-create).
//   5. REFUNDED payload (same transaction) → mentee paused, refund_pause
//      message attempted (falls back silently when messages.* missing).
//
// All artefacts are written to a temp scriptDir to keep the live
// `mentees/` directory clean.

import { mkdirSync, rmSync, existsSync, readFileSync, cpSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { tmpdir } from 'os';
import { configureTest, _resetTestState, readOutbox, clearOutbox } from '../lib/whatsappAdapter.js';
import { handleHotmartWebhook, parseBody, normalizePayload } from '../lib/hotmartWebhook.js';
import { loadSilver } from '../lib/silverStore.js';

const REAL_SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)).replace(/\/scripts$/, '');
const TMP = join(tmpdir(), `ecobot-hotmart-test-${Date.now()}`);

function setup() {
  if (existsSync(TMP)) rmSync(TMP, { recursive: true, force: true });
  mkdirSync(TMP, { recursive: true });
  // Need lessons.json + tasks.json + transcripts visible to silverStore catalog calls.
  cpSync(join(REAL_SCRIPT_DIR, 'data'), join(TMP, 'data'), { recursive: true });
  configureTest();
  _resetTestState();
}

function fixture(name) {
  return readFileSync(join(REAL_SCRIPT_DIR, 'scripts', 'fixtures', name), 'utf8');
}

function withHottok(jsonStr, hottok) {
  const parsed = JSON.parse(jsonStr);
  parsed.hottok = hottok;
  return Buffer.from(JSON.stringify(parsed));
}

let pass = 0, fail = 0;
function check(label, ok, detail) {
  if (ok) { pass += 1; console.log(`✅ ${label}`); }
  else    { fail += 1; console.log(`❌ ${label}${detail ? ' — ' + detail : ''}`); }
}

async function main() {
  setup();

  const headers = { 'content-type': 'application/json' };

  // 1. No env config → 503
  delete process.env.HOTMART_HOTTOK;
  let r = await handleHotmartWebhook(TMP, { rawBody: withHottok(fixture('hotmart-approved.json'), 'whatever'), headers });
  check('no env returns 503', r.status === 503, `got ${r.status}`);

  // Set env for remaining tests
  process.env.HOTMART_HOTTOK = 'TEST_HOTTOK_SECRET';

  // 2. Bad hottok → 401
  r = await handleHotmartWebhook(TMP, { rawBody: withHottok(fixture('hotmart-approved.json'), 'WRONG'), headers });
  check('bad hottok returns 401', r.status === 401, `got ${r.status}`);

  // 3. Good APPROVED payload → 200 + mentee created
  clearOutbox();
  r = await handleHotmartWebhook(TMP, { rawBody: withHottok(fixture('hotmart-approved.json'), 'TEST_HOTTOK_SECRET'), headers });
  check('approved returns 200', r.status === 200, `got ${r.status} body=${JSON.stringify(r.body)}`);
  check('approved action is start', r.body.action === 'start');
  const slug = r.body.slug;
  check('slug derived from phone', slug === 'silver-41791234567', `got ${slug}`);

  const state = loadSilver(TMP, slug);
  check('silver.json created', !!state);
  check('paymentStatus=approved', state?.paymentStatus === 'approved');
  check('accessLevel=course_only', state?.accessLevel === 'course_only');
  check('coachJid=null (unassigned)', state?.coachJid === null);
  check('hotmartTransactionId set', state?.hotmartTransactionId === 'HP_TX_TEST_APPROVED_001');
  check('locale=en (Swiss number)', state?.locale === 'en');
  check('groupJid set', !!state?.groupJid);
  const expDays = state?.accessExpiresAt
    ? Math.round((new Date(state.accessExpiresAt) - new Date(state.startedAt)) / 86_400_000)
    : null;
  check('accessExpiresAt ~90d out', expDays === 90, `got ${expDays}`);

  const outbox = readOutbox();
  const groupCreate = outbox.find(o => o.kind === 'group-create');
  check('group create captured in test outbox', !!groupCreate);
  const welcome = outbox.find(o => o.text && o.text.includes('Welcome to Silver'));
  check('welcome message sent', !!welcome);
  check('welcome has NO Job-bot link', welcome && !/jobbot|career.plan/i.test(welcome.text));
  check('welcome mentions members area', welcome && welcome.text.includes('Members Area'));

  // 4. Replay same payload → duplicate
  r = await handleHotmartWebhook(TMP, { rawBody: withHottok(fixture('hotmart-approved.json'), 'TEST_HOTTOK_SECRET'), headers });
  check('replay returns 200 duplicate', r.status === 200 && r.body.duplicate === true);

  // 5. REFUNDED → paused
  clearOutbox();
  r = await handleHotmartWebhook(TMP, { rawBody: withHottok(fixture('hotmart-refunded.json'), 'TEST_HOTTOK_SECRET'), headers });
  check('refund returns 200', r.status === 200);
  check('refund action=pause', r.body.action === 'pause');
  const refunded = loadSilver(TMP, slug);
  check('paymentStatus=refunded', refunded?.paymentStatus === 'refunded');
  check('pausedAt set', !!refunded?.pausedAt);
  check('pausedReason=hotmart:refunded', refunded?.pausedReason === 'hotmart:refunded');

  // 6. Audit log exists
  const auditPath = join(TMP, 'data', 'hotmart', 'webhook-log.jsonl');
  check('audit log written', existsSync(auditPath));
  const auditLines = readFileSync(auditPath, 'utf8').trim().split('\n');
  check('audit log has multiple entries', auditLines.length >= 4, `got ${auditLines.length}`);

  // 7. Body parser handles form-urlencoded too
  const formPayload = 'hottok=TEST_HOTTOK_SECRET&prod=4567890&off=OFFER&name=FormTest&email=form%40x.com&phone=%2B5511987654321&transaction=HP_FORM_001&status=approved';
  const parsed = parseBody(Buffer.from(formPayload), 'application/x-www-form-urlencoded');
  check('form parse: hottok', parsed.hottok === 'TEST_HOTTOK_SECRET');
  check('form parse: phone decoded', parsed.phone === '+5511987654321');
  const norm = normalizePayload(parsed);
  check('v1 normalize: status', norm.status === 'approved');
  check('v1 normalize: phone digits', norm.buyerPhone === '5511987654321');

  rmSync(TMP, { recursive: true, force: true });
  console.log(`\n${pass} passed, ${fail} failed`);
  process.exit(fail === 0 ? 0 : 1);
}

main().catch(e => { console.error(e); process.exit(1); });
