// Auth flow helpers — register / confirm / login / refresh / logout.
// All requests go through lib/http.js; OTP comes from lib/otp.js.
import { post } from './http.js';
import { checkEnvelope } from './http.js';
import { cfg, unique } from './config.js';
import { fetchEmailOTP } from './otp.js';
import { generateTOTP } from './totp.js';

// makeIdentity returns a unique (email, password, display_name) tuple for the
// current VU iteration. Use to avoid collisions in concurrent runs.
export function makeIdentity(prefix = 'k6') {
  const slug = unique(prefix);
  return {
    email: `${slug}@k6.local`,
    password: 'K6-StrongPass-1!',
    displayName: `K6 ${slug}`,
  };
}

// registerEmail calls POST /api/v1/auth/register and returns the parsed
// RegisterData (challenge_id, expires_in, uid).
export function registerEmail({ tenantSlug = cfg.tenantSlug, inviteCode = cfg.inviteCode, email, password, displayName, language = 'zh-TW', termsVersion = '2025-01-01', marketingOptIn = false } = {}) {
  const res = post('/api/v1/auth/register', {
    tenant_slug: tenantSlug,
    invite_code: inviteCode,
    email,
    password,
    display_name: displayName,
    language,
    accept_terms_version: termsVersion,
    marketing_opt_in: marketingOptIn,
  });
  const body = checkEnvelope(res, 'POST /auth/register');
  if (!body.data || !body.data.challenge_id) {
    throw new Error(`register: missing challenge_id in ${res.body}`);
  }
  return body.data;
}

// confirmRegister fetches the OTP from MailHog then calls
// POST /api/v1/auth/register/confirm. Returns AuthTokenData.
export function confirmRegister({ tenantSlug = cfg.tenantSlug, email, challengeId }) {
  const code = fetchEmailOTP(email);
  const res = post('/api/v1/auth/register/confirm', {
    tenant_slug: tenantSlug,
    challenge_id: challengeId,
    code,
  });
  const body = checkEnvelope(res, 'POST /auth/register/confirm');
  if (!body.data || !body.data.access_token) {
    throw new Error(`register/confirm: missing access_token in ${res.body}`);
  }
  return body.data;
}

// resendRegister calls POST /api/v1/auth/register/resend.
// Returns RegisterData (challenge_id, expires_in, uid).
export function resendRegister({ tenantSlug = cfg.tenantSlug, challengeId }) {
  const res = post('/api/v1/auth/register/resend', {
    tenant_slug: tenantSlug,
    challenge_id: challengeId,
  });
  return checkEnvelope(res, 'POST /auth/register/resend').data;
}

// registerResume calls POST /api/v1/auth/register/resume.
export function registerResume({ tenantSlug = cfg.tenantSlug, email }) {
  const res = post('/api/v1/auth/register/resume', {
    tenant_slug: tenantSlug,
    email,
  });
  return checkEnvelope(res, 'POST /auth/register/resume').data;
}

// passwordForgot calls POST /api/v1/auth/password/forgot.
export function passwordForgot({ tenantSlug = cfg.tenantSlug, email }) {
  const res = post('/api/v1/auth/password/forgot', {
    tenant_slug: tenantSlug,
    email,
  });
  return checkEnvelope(res, 'POST /auth/password/forgot').data;
}

// passwordReset calls POST /api/v1/auth/password/reset.
export function passwordReset({
  tenantSlug = cfg.tenantSlug,
  challengeId,
  code,
  newPassword,
}) {
  const res = post('/api/v1/auth/password/reset', {
    tenant_slug: tenantSlug,
    challenge_id: challengeId,
    code,
    new_password: newPassword,
  });
  return checkEnvelope(res, 'POST /auth/password/reset').data;
}

// loginStep calls POST /api/v1/auth/login and returns LoginData (tokens or MFA challenge).
export function loginStep({ tenantSlug = cfg.tenantSlug, email, password }) {
  const res = post('/api/v1/auth/login', {
    tenant_slug: tenantSlug,
    email,
    password,
  });
  return checkEnvelope(res, 'POST /auth/login').data;
}

// loginMfaConfirm completes login after MFA challenge.
export function loginMfaConfirm({ tenantSlug = cfg.tenantSlug, challengeId, code }) {
  const res = post('/api/v1/auth/login/mfa', {
    tenant_slug: tenantSlug,
    challenge_id: challengeId,
    code,
  });
  return checkEnvelope(res, 'POST /auth/login/mfa').data;
}

// loginExpectMFA returns LoginData when mfa_required=true.
export function loginExpectMFA({ tenantSlug = cfg.tenantSlug, email, password }) {
  const data = loginStep({ tenantSlug, email, password });
  if (!data.mfa_required) {
    throw new Error(`loginExpectMFA: expected mfa_required, got ${JSON.stringify(data)}`);
  }
  if (!data.mfa_challenge_id) {
    throw new Error('loginExpectMFA: missing mfa_challenge_id');
  }
  return data;
}

// login calls POST /api/v1/auth/login. When TOTP is enrolled, pass totpCode or otpauthUrl.
export function login({ tenantSlug = cfg.tenantSlug, email, password, totpCode, otpauthUrl } = {}) {
  const step = loginStep({ tenantSlug, email, password });
  if (step.mfa_required) {
    let code = totpCode;
    if (!code && otpauthUrl) {
      code = generateTOTP(otpauthUrl);
    }
    if (!code) {
      throw new Error('login: MFA required but totpCode/otpauthUrl not provided');
    }
    const tokens = loginMfaConfirm({
      tenantSlug,
      challengeId: step.mfa_challenge_id,
      code,
    });
    if (!tokens.access_token) {
      throw new Error('login/mfa: missing access_token');
    }
    return tokens;
  }
  if (!step.access_token) {
    throw new Error(`login: missing access_token in ${JSON.stringify(step)}`);
  }
  return step;
}

// refreshToken calls POST /api/v1/auth/token/refresh.
export function refreshToken({ refreshToken }) {
  const res = post('/api/v1/auth/token/refresh', { refresh_token: refreshToken });
  const body = checkEnvelope(res, 'POST /auth/token/refresh');
  if (!body.data || !body.data.access_token) {
    throw new Error(`token/refresh: missing access_token in ${res.body}`);
  }
  return body.data;
}

// logout calls POST /api/v1/auth/logout (requires Bearer access_token).
export function logout({ accessToken }) {
  const res = post('/api/v1/auth/logout', null, { Authorization: `Bearer ${accessToken}` });
  const body = checkEnvelope(res, 'POST /auth/logout');
  return body.data;
}

// registerAndConfirm is the most common building block: makes an identity,
// runs register → confirm, returns { identity, tokens, registerData }.
export function registerAndConfirm({ tenantSlug = cfg.tenantSlug, inviteCode = cfg.inviteCode } = {}) {
  const identity = makeIdentity();
  const reg = registerEmail({
    tenantSlug,
    inviteCode,
    email: identity.email,
    password: identity.password,
    displayName: identity.displayName,
  });
  const tokens = confirmRegister({ tenantSlug, email: identity.email, challengeId: reg.challenge_id });
  return { identity, tokens, registerData: reg };
}
