const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');

const source = fs.readFileSync('content/signup-page.js', 'utf8');

function extractFunction(name) {
  const markers = [`async function ${name}(`, `function ${name}(`];
  const start = markers
    .map((marker) => source.indexOf(marker))
    .find((index) => index >= 0);
  if (start < 0) {
    throw new Error(`missing function ${name}`);
  }

  let parenDepth = 0;
  let signatureEnded = false;
  let braceStart = -1;
  for (let i = start; i < source.length; i += 1) {
    const ch = source[i];
    if (ch === '(') {
      parenDepth += 1;
    } else if (ch === ')') {
      parenDepth -= 1;
      if (parenDepth === 0) {
        signatureEnded = true;
      }
    } else if (ch === '{' && signatureEnded) {
      braceStart = i;
      break;
    }
  }
  if (braceStart < 0) {
    throw new Error(`missing body for function ${name}`);
  }

  let depth = 0;
  let end = braceStart;
  for (; end < source.length; end += 1) {
    const ch = source[end];
    if (ch === '{') depth += 1;
    if (ch === '}') {
      depth -= 1;
      if (depth === 0) {
        end += 1;
        break;
      }
    }
  }

  return source.slice(start, end);
}

const bundle = extractFunction('step6LoginFromPasswordPage');

function createApi() {
  return new Function(`
${bundle}
return { step6LoginFromPasswordPage };
`)();
}

function cleanupGlobals() {
  delete globalThis.normalizeStep6Snapshot;
  delete globalThis.inspectLoginAuthState;
  delete globalThis.log;
  delete globalThis.step6SwitchToOneTimeCodeLogin;
  delete globalThis.createStep6RecoverableResult;
  delete globalThis.fillInput;
  delete globalThis.humanPause;
  delete globalThis.sleep;
  delete globalThis.triggerLoginSubmitAction;
  delete globalThis.waitForStep6PasswordSubmitTransition;
}

test('step6LoginFromPasswordPage switches to one-time-code login when password is missing but switch trigger exists', async () => {
  const api = createApi();
  const logs = [];
  const snapshot = {
    state: 'password_page',
    passwordInput: { id: 'password' },
    switchTrigger: { id: 'otp' },
  };

  globalThis.normalizeStep6Snapshot = (value) => value;
  globalThis.inspectLoginAuthState = () => snapshot;
  globalThis.log = (message, level = 'info', options = {}) => {
    logs.push({ message, level, step: options.step, stepKey: options.stepKey });
  };
  globalThis.step6SwitchToOneTimeCodeLogin = async (payload, value) => {
    assert.deepStrictEqual(payload, { email: 'user@example.com', password: '' });
    assert.strictEqual(value, snapshot);
    return { step6Outcome: 'success', via: 'switch_to_one_time_code_login' };
  };
  globalThis.createStep6RecoverableResult = () => {
    throw new Error('should not create recoverable result when switch trigger exists');
  };
  globalThis.fillInput = () => {
    throw new Error('should not fill password when password is missing');
  };
  globalThis.humanPause = async () => {};
  globalThis.sleep = async () => {};
  globalThis.triggerLoginSubmitAction = async () => {};
  globalThis.waitForStep6PasswordSubmitTransition = async () => {
    throw new Error('should not submit password when password is missing');
  };

  try {
    const result = await api.step6LoginFromPasswordPage({ email: 'user@example.com', password: '' }, snapshot);

    assert.deepStrictEqual(result, { step6Outcome: 'success', via: 'switch_to_one_time_code_login' });
    assert.deepStrictEqual(logs, [
      { message: '当前未提供密码，改走一次性验证码登录。', level: 'warn', step: 7, stepKey: 'oauth-login' },
    ]);
  } finally {
    cleanupGlobals();
  }
});

test('step6LoginFromPasswordPage returns a recoverable result when password is missing and no one-time-code trigger exists', async () => {
  const api = createApi();
  const snapshot = {
    state: 'password_page',
    passwordInput: { id: 'password' },
    switchTrigger: null,
  };

  globalThis.normalizeStep6Snapshot = (value) => value;
  globalThis.inspectLoginAuthState = () => snapshot;
  globalThis.log = () => {};
  globalThis.step6SwitchToOneTimeCodeLogin = async () => {
    throw new Error('should not switch without a one-time-code trigger');
  };
  globalThis.createStep6RecoverableResult = (reason, stateSnapshot, details) => ({
    step6Outcome: 'recoverable',
    reason,
    stateSnapshot,
    ...details,
  });
  globalThis.fillInput = () => {
    throw new Error('should not fill password when password is missing');
  };
  globalThis.humanPause = async () => {};
  globalThis.sleep = async () => {};
  globalThis.triggerLoginSubmitAction = async () => {};
  globalThis.waitForStep6PasswordSubmitTransition = async () => {
    throw new Error('should not submit password when password is missing');
  };

  try {
    const result = await api.step6LoginFromPasswordPage({ email: 'user@example.com', password: '' }, snapshot);

    assert.deepStrictEqual(result, {
      step6Outcome: 'recoverable',
      reason: 'missing_password_and_one_time_code_trigger',
      stateSnapshot: snapshot,
      message: '登录时未提供密码，且当前页面没有可用的一次性验证码登录入口。',
    });
  } finally {
    cleanupGlobals();
  }
});
