import { AuthRequiredError, TimeoutError, getErrorMessage } from '@jackwener/opencli/errors';
import { cli, Strategy } from '@jackwener/opencli/registry';

const DEFAULT_TIMEOUT_SECONDS = 300;
const POLL_INTERVAL_MS = 2000;

function normalizeIdentity(site, identity) {
  const row = identity && typeof identity === 'object' && !Array.isArray(identity)
    ? identity
    : {};
  return { logged_in: true, site, ...row };
}

function isAuthRequired(error) {
  return error instanceof AuthRequiredError;
}

async function tryProbe(config, page, phase) {
  const probe = phase === 'poll' && config.poll ? config.poll : config.verify;
  return normalizeIdentity(config.site, await probe(page, { phase }));
}

function authHint(config) {
  return `Run \`opencli ${config.site} login\` to open the login page, then retry.`;
}

function commandColumns(config) {
  const identityColumns = config.columns ?? ['id', 'username', 'name'];
  return ['logged_in', 'site', ...identityColumns];
}

function normalizeQuickCheck(result) {
  if (typeof result === 'boolean') return { logged_in: result };
  if (result && typeof result === 'object' && !Array.isArray(result)) {
    return { logged_in: !!result.logged_in, ...result };
  }
  return { logged_in: false };
}

function normalizeRefreshResult(result) {
  if (result && typeof result === 'object' && !Array.isArray(result)) return result;
  return { touched: true };
}

export function registerSiteAuthCommands(config) {
  if (!config?.site || !config?.domain || !config?.loginUrl || typeof config.verify !== 'function') {
    throw new Error('registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)');
  }

  cli({
    site: config.site,
    name: 'whoami',
    access: 'read',
    description: config.whoamiDescription ?? `Show the current logged-in ${config.site} account`,
    domain: config.domain,
    strategy: Strategy.COOKIE,
    browser: true,
    navigateBefore: false,
    siteSession: 'persistent',
    args: [],
    columns: commandColumns(config),
    authStatus: {
      ...(typeof config.quickCheck === 'function'
        ? { quickCheck: async (page) => normalizeQuickCheck(await config.quickCheck(page)) }
        : {}),
      ...(typeof config.refresh === 'function'
        ? { refresh: async (page, kwargs) => normalizeRefreshResult(await config.refresh(page, kwargs)) }
        : {}),
    },
    func: async (page) => tryProbe(config, page, 'identity'),
  });

  cli({
    site: config.site,
    name: 'login',
    access: 'write',
    description: config.loginDescription ?? `Open ${config.site} login and wait until the browser session is authenticated`,
    domain: config.domain,
    strategy: Strategy.COOKIE,
    browser: true,
    navigateBefore: false,
    defaultWindowMode: 'foreground',
    siteSession: 'persistent',
    args: [
      { name: 'timeout', type: 'int', default: DEFAULT_TIMEOUT_SECONDS, help: 'Maximum seconds to wait for the user to finish login' },
    ],
    columns: ['status', ...commandColumns(config)],
    func: async (page, kwargs) => {
      try {
        return { status: 'already_logged_in', ...await tryProbe(config, page, 'identity') };
      } catch (error) {
        if (!isAuthRequired(error)) throw error;
      }

      await page.goto(config.loginUrl);
      const timeoutSeconds = Number(kwargs.timeout ?? DEFAULT_TIMEOUT_SECONDS);
      const deadline = Date.now() + timeoutSeconds * 1000;
      let lastAuthMessage = '';

      while (Date.now() < deadline) {
        await page.wait(Math.min(POLL_INTERVAL_MS / 1000, Math.max(0.2, (deadline - Date.now()) / 1000)));
        try {
          const identity = await tryProbe(config, page, 'poll');
          return { status: 'login_complete', ...identity };
        } catch (error) {
          if (!isAuthRequired(error)) throw error;
          lastAuthMessage = getErrorMessage(error);
        }
      }

      throw new TimeoutError(
        `${config.site} login`,
        timeoutSeconds,
        lastAuthMessage ? `${authHint(config)} Last auth check: ${lastAuthMessage}` : authHint(config),
      );
    },
  });
}
