// OTP retrieval helpers (P1 skeleton).
//
// fetchEmailOTP(email)   → reads the latest MailHog mail for `email`, scans body
//                          for a 6-digit code. Full implementation in P2 lib.
// fetchSMSOTP(phone)     → reads "dev:notification:last:sms:<phone>" from Redis
//                          (k6/experimental/redis). Implementation in P3.
//
// Both poll for up to cfg.otpPollTimeoutMs because Notifier writes happen
// asynchronously (Send goroutine + Redis hook).
import http from 'k6/http';
import { sleep } from 'k6';
// k6 v2.0+ uses 'k6/x/redis' (the experimental module was promoted). The
// default export is a namespace; the constructor is `redis.Client`.
import redis from 'k6/x/redis';
import { cfg } from './config.js';

const SIX_DIGITS = /\b(\d{6})\b/;
// CSS hex colors (#aabbcc) look like 6-digit OTPs to a naive regex; strip
// them before scanning. We also strip quoted-printable soft-line-breaks
// (`=\n`) since MailHog returns bodies QP-encoded.
const CSS_HEX = /#[0-9a-fA-F]{6}\b/g;
const QP_SOFT_BREAK = /=\r?\n/g;

function sleepMs(ms) {
  sleep(ms / 1000);
}

// extractOTPFromText returns the LAST 6-digit number found (after stripping
// CSS hex colors). "Last" because the OTP is typically rendered near the
// bottom of the email body, after the header/branding markup.
export function extractOTPFromText(text) {
  if (!text) return '';
  const cleaned = String(text).replace(QP_SOFT_BREAK, '').replace(CSS_HEX, '');
  const all = cleaned.match(/\b\d{6}\b/g);
  if (!all || all.length === 0) return '';
  return all[all.length - 1];
}

// fetchEmailOTP polls MailHog's /api/v2/search?kind=to&query=<email>
// until a 6-digit OTP can be parsed out of the most recent message body.
//
// opts.since (ms epoch, default 0): ignore mails delivered before this ts —
//   needed when the same address has multiple OTPs in MailHog (e.g. register
//   then email-verify), so the verify step picks up the new mail rather than
//   the register one.
// opts.limit (default 5): number of latest mails to inspect each poll.
export function fetchEmailOTP(email, opts = {}) {
  const since = opts.since || 0;
  const limit = opts.limit || 5;
  const deadline = Date.now() + cfg.otpPollTimeoutMs;
  const u = `${cfg.mailhogUrl}/api/v2/search?kind=to&query=${encodeURIComponent(email)}&start=0&limit=${limit}`;
  let last = '';
  while (Date.now() < deadline) {
    const res = http.get(u);
    if (res.status === 200) {
      try {
        const body = JSON.parse(res.body);
        const items = (body && body.items) || [];
        for (const item of items) {
          const created = item.Created ? Date.parse(item.Created) : 0;
          if (created && created < since) continue;
          const candidates = [
            item.Content && item.Content.Body,
            item.MIME && item.MIME.Parts && item.MIME.Parts.map((p) => p.Body).join('\n'),
          ].filter(Boolean);
          for (const c of candidates) {
            const code = extractOTPFromText(c);
            if (code) return code;
          }
        }
        if (items.length > 0) last = `no 6-digit code in ${items.length} items (since=${since})`;
      } catch (e) {
        last = `parse-error: ${e}`;
      }
    } else {
      last = `mailhog status ${res.status}`;
    }
    sleepMs(cfg.otpPollIntervalMs);
  }
  throw new Error(`fetchEmailOTP(${email}) timed out: ${last}`);
}

// fetchSMSOTP polls Redis key "dev:notification:last:sms:<phone>" set by the
// mock SMS sender (see internal/model/notification/provider/sms/mock_sender.go).
//
// opts.prevBody: when supplied, the poll ignores writes whose body equals
//   prevBody. This is essential when the same phone has two OTPs in a single
//   test (e.g. register-resend or re-verify); the caller passes the body it
//   already consumed so the helper waits for the next one.
//
// Requires k6 v2.0+ which exposes the redis client at k6/x/redis (the
// `k6/experimental/redis` module was removed in v2.0). Statically imported
// above so this works in default k6 compatibility mode.
export async function fetchSMSOTP(phone, opts = {}) {
  const prev = opts.prevBody || '';
  const [host, port] = cfg.redisAddr.split(':');
  const client = new redis.Client({ socket: { host: host, port: parseInt(port || '6379', 10) } });
  const key = `dev:notification:last:sms:${phone}`;

  const deadline = Date.now() + cfg.otpPollTimeoutMs;
  while (Date.now() < deadline) {
    const body = await client.get(key);
    if (body && body !== prev) {
      const code = extractOTPFromText(body);
      if (code) return { code, body };
    }
    sleepMs(cfg.otpPollIntervalMs);
  }
  throw new Error(`fetchSMSOTP(${phone}) timed out`);
}
