/**
 * Thin wrapper around the internal backend for test setup / fixtures.
 *
 * The CC test suite needs to seed and clean up rows directly via the API
 * (rather than via the UI) for tests that depend on pre-existing state —
 * e.g. "Failed Pairings page renders rows" needs at least one row to exist.
 *
 * All requests:
 *   - go to BACKEND_INTERNAL_URL (default http://localhost:3001)
 *   - send credentials: 'include' so cookies (refresh token) round-trip
 *   - automatically attach Authorization: Bearer <access_token> after login
 */

const BACKEND_URL = process.env.BACKEND_INTERNAL_URL || 'http://localhost:3001'
const TEST_EMAIL = process.env.CC_TEST_EMAIL || 'test-operator@example.com'
const TEST_PASSWORD = process.env.CC_TEST_PASSWORD || 'changeme'

let cachedAccessToken = null
let cachedCookieHeader = null

/**
 * Logs into the backend via the real CC login route and caches the access
 * token + Set-Cookie value for subsequent calls. Token is cached for the
 * whole worker lifetime — for the slim CC suite that's plenty.
 */
async function loginToBackend() {
  if (cachedAccessToken) return cachedAccessToken

  const res = await fetch(`${BACKEND_URL}/internal/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email: TEST_EMAIL, password: TEST_PASSWORD }),
  })

  if (!res.ok) {
    const body = await res.text().catch(() => '')
    throw new Error(`Backend login failed (${res.status}): ${body}`)
  }

  const json = await res.json()
  cachedAccessToken = json?.data?.access_token
  if (!cachedAccessToken) {
    throw new Error(`Backend login response missing access_token: ${JSON.stringify(json)}`)
  }

  // node-fetch returns multi-value Set-Cookie via .getSetCookie() in newer
  // Node versions. Fall back to .get() for older runtimes.
  const setCookie =
    typeof res.headers.getSetCookie === 'function'
      ? res.headers.getSetCookie()
      : [res.headers.get('set-cookie')].filter(Boolean)
  cachedCookieHeader = setCookie.map((c) => c.split(';')[0]).join('; ') || null

  return cachedAccessToken
}

/**
 * Generic authenticated request to the internal backend.
 *
 * @param {string} path        — path beginning with `/internal/...`
 * @param {RequestInit} [init] — fetch options (method, body, headers)
 * @returns {Promise<any>}     — parsed JSON response body (whole envelope)
 */
export async function backendRequest(path, init = {}) {
  const token = await loginToBackend()
  const headers = {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${token}`,
    ...(init.headers || {}),
  }
  if (cachedCookieHeader) headers.Cookie = cachedCookieHeader

  const res = await fetch(`${BACKEND_URL}${path}`, { ...init, headers })
  const text = await res.text()
  let json
  try {
    json = text ? JSON.parse(text) : null
  } catch {
    json = { raw: text }
  }

  if (!res.ok) {
    const errMsg = json?.error?.message || text || res.statusText
    throw new Error(`backend ${init.method || 'GET'} ${path} -> ${res.status}: ${errMsg}`)
  }
  return json
}

// ---------------------------------------------------------------------------
// Settings helpers — read/write CC config rows used by settings.spec.js
// ---------------------------------------------------------------------------

/** Read current `payment_session_timeout_minutes` value. */
export async function getPaymentSessionTimeout() {
  const json = await backendRequest('/internal/config/payment-session-timeout')
  return json?.data?.payment_session_timeout_minutes
}

/** Force-set `payment_session_timeout_minutes` (useful for test reset). */
export async function setPaymentSessionTimeout(minutes) {
  return backendRequest('/internal/config/payment-session-timeout', {
    method: 'PATCH',
    body: JSON.stringify({ payment_session_timeout_minutes: minutes }),
  })
}

/** Read current `extension_default_action_on_timeout` value. */
export async function getExtensionDefaultAction() {
  const json = await backendRequest('/internal/config/extension-default-action')
  return json?.data?.extension_default_action_on_timeout
}

/** Force-set `extension_default_action_on_timeout` (useful for test reset). */
export async function setExtensionDefaultAction(action) {
  return backendRequest('/internal/config/extension-default-action', {
    method: 'PATCH',
    body: JSON.stringify({ extension_default_action_on_timeout: action }),
  })
}

// ---------------------------------------------------------------------------
// Failed-pairings helpers
// ---------------------------------------------------------------------------

/**
 * Fetch failed-pairings rows (optionally filtered). Used to assert the page
 * is reading what we just inserted, and to discover a real customer/mitra ID
 * if needed.
 */
export async function listFailedPairings({ causeTags = [], limit = 50, offset = 0 } = {}) {
  const params = new URLSearchParams()
  for (const tag of causeTags) params.append('cause_tags', tag)
  params.set('limit', String(limit))
  params.set('offset', String(offset))
  const json = await backendRequest(`/internal/failed-pairings?${params}`)
  return json?.data
}

/**
 * Seed a fixture failed-pairing row.
 *
 * NOTE: There is no public "create failed pairing" endpoint — production
 * rows are inserted by the backend pairing service when a real pairing
 * fails. For e2e tests we expose a thin test-only seed by reusing the
 * /internal/failed-pairings list to verify whatever rows already exist.
 *
 * If the suite needs guaranteed-fresh rows, the user should add a small
 * test-only seed route to the backend (e.g. POST /internal/_test/seed-failed-pairing)
 * gated behind NODE_ENV !== 'production'. This helper currently:
 *   - returns the existing row count, OR
 *   - throws a clear error so the test can be skipped with a TODO.
 *
 * @returns {Promise<{ total: number, rows: any[] }>}
 */
export async function ensureAtLeastOneFailedPairing() {
  const data = await listFailedPairings({ limit: 1 })
  if ((data?.total ?? 0) === 0) {
    throw new Error(
      'No failed-pairings rows present in the database. ' +
      'Either trigger one via the real pairing flow or add a backend test-seed route.',
    )
  }
  return data
}
