const express = require('express');
const initTotp = require('./totp');
const initKeys = require('./keys');
const initSessionHandlers = require('./session-handlers');
const initSsoGate = require('./sso-gate');
const initLogin = require('./login');
const initAdmin = require('./admin');
const { createAuthProviderRegistry } = require('../../src/auth/providers');
const { createUserStore } = require('../../src/security/user-store');

/**
 * Auth routes aggregator
 * Assembles all auth sub-routes with their dependencies
 * @param {Object} ctx - Application context (for backward compatibility)
 * @returns {express.Router}
 */

/**
 * Pull the SMTP/email provider config from whichever source has it.
 *
 * Resolution order:
 *   1. ctx.emailProviderConfig  — explicit override (operator or env)
 *   2. ctx.notification.getConfig?.().providers.email  — reuse the same
 *      SMTP settings notifications use. This is the "magic" — operators
 *      configure SMTP once for system notifications and email-auth picks
 *      it up automatically.
 *   3. null — provider will operate in dev-console fallback mode.
 */
function _extractEmailConfig(ctx) {
  if (ctx.emailProviderConfig && typeof ctx.emailProviderConfig === 'object') {
    return ctx.emailProviderConfig;
  }
  const n = ctx.notification;
  if (n && typeof n.getConfig === 'function') {
    const cfg = n.getConfig();
    if (cfg && cfg.providers && cfg.providers.email) return cfg.providers.email;
  }
  return null;
}

module.exports = function(ctx) {
  const router = express.Router();

  // DC-048: opt-in user store. Only instantiated when the operator has
  // explicitly enabled email auth in siteConfig. The default for new
  // installs is "no user-store, no allowlist, no admin invites" — the
  // legacy single-user TOTP flow. Operators who turn email auth on
  // (siteConfig.authProviders.email.enabled = true) opt into multi-user.
  // Once opted in, the first email to log in is the bootstrap admin.
  const platformPaths = ctx.platformPaths || require('../../platform-paths');
  let userStore = null;

  const _emailExplicitlyEnabled =
    ctx.siteConfig &&
    ctx.siteConfig.authProviders &&
    ctx.siteConfig.authProviders.email &&
    ctx.siteConfig.authProviders.email.enabled === true;

  if (_emailExplicitlyEnabled) {
    userStore = createUserStore({
      dataDir: platformPaths.dataDir,
      log: ctx.log,
    });
    ctx.userStore = userStore;
    ctx.log && ctx.log.info && ctx.log.info('user', 'multi-user mode enabled (email auth on)');
  } else {
    ctx.log && ctx.log.info && ctx.log.info('user', 'single-user mode (email auth not enabled — set siteConfig.authProviders.email.enabled = true to opt into multi-user)');
  }

  // Extract dependencies from context
  const deps = {
    authManager: ctx.authManager,
    credentialManager: ctx.credentialManager,
    totpConfig: ctx.totpConfig,
    saveTotpConfig: ctx.saveTotpConfig,
    session: ctx.session,
    asyncHandler: ctx.asyncHandler,
    errorResponse: ctx.errorResponse,
    log: ctx.log,
    // Additional deps for sso-gate
    fetchT: ctx.fetchT,
    getServiceById: ctx.getServiceById,
    licenseManager: ctx.licenseManager,
    servicesStateManager: ctx.servicesStateManager,
    renewCSRFToken: ctx.middlewareResult?.renewCSRFToken,
    // For DC-046 pluggable auth providers (EmailMagicLink, OIDC, …).
    // Pass-through — providers like the EmailMagicLinkProvider need
    // notificationManager for SMTP delivery, plus the siteConfig for
    // building verification links.
    notificationManager: ctx.notification,
    siteConfig: ctx.siteConfig,
    // DC-047: data-directory resolution for the email-token JSON store.
    platformPaths,
    // DC-048: user store for allowlist + bootstrap. Null when email
    // auth is disabled — providers fall back to "allow everyone" legacy
    // behavior (DC-046/047 semantics).
    userStore,
  };

  const { getAppSession, appSessionCache } = initSessionHandlers(deps);

  // DC-046: pluggable auth provider registry. The TOTP provider is wired
  // here against the existing totpConfig / saveTotpConfig objects so it
  // behaves identically to the legacy /api/v1/totp/* routes mounted below.
  const registry = createAuthProviderRegistry(
    {
      credentialManager: ctx.credentialManager,
      session: ctx.session,
      saveTotpConfig: ctx.saveTotpConfig,
      config: { totp: ctx.totpConfig, email: ctx.emailProviderConfig || { enabled: false } },
      log: ctx.log,
      renewCSRFToken: ctx.middlewareResult?.renewCSRFToken,
      // DC-047: EmailMagicLinkProvider needs SMTP config + a public URL
      // resolver + the data dir for the token store. All three come from
      // existing global config — no new config knobs required.
      emailConfig: _extractEmailConfig(ctx),
      siteConfig: ctx.siteConfig || {},
      platformPaths: deps.platformPaths,
      // DC-048: user store shared by every provider for allowlist checks
      // and the bootstrap-admin-on-first-login rule.
      userStore: deps.userStore,
      // DC-052: license manager so providers can gate Pro-only flows
      // (e.g. magic-link signup that crosses the 3-user cap).
      licenseManager: ctx.licenseManager,
    },
    ctx.siteConfig
  );
  ctx.authProviders = registry; // exposed for /api/v1/auth/methods, etc.

  // NEW (DC-046): pluggable /api/v1/auth/login/* routes. Frontends should
  // migrate here over time — the legacy /api/v1/totp/* routes below stay
  // for back-compat. Mounted under `/auth` so internal paths
  // (`/login/methods`, `/disable/:provider`) resolve at the canonical
  // `/api/v1/auth/login/*` and `/api/v1/auth/disable/*` URLs that match
  // PUBLIC_ROUTES and the documented login UI contract.
  router.use('/auth', initLogin({
    registry,
    asyncHandler: ctx.asyncHandler,
    errorResponse: ctx.errorResponse,
    log: ctx.log,
  }));

  router.use(initTotp(deps));
  router.use(initKeys(deps));
  router.use(initSsoGate({ ...deps, getAppSession, appSessionCache }));

  // DC-048: mount admin routes ONLY when the user-store was instantiated
  // (i.e. email auth is enabled). Single-user installs don't see /me,
  // /admin/*, or /invites/* at all. The route paths simply don't exist
  // so a request to /api/v1/auth/me returns 404 from the apiRouter.
  if (userStore) {
    // DC-052: pass licenseManager + userStore through so the tier-gate
    // middleware can read them. Both are optional — the gate short-
    // circuits when licenseManager is absent.
    const adminRouter = initAdmin({
      asyncHandler: ctx.asyncHandler,
      errorResponse: ctx.errorResponse,
      log: ctx.log,
      session: ctx.session,
      licenseManager: ctx.licenseManager,
      userStore,
    });

    // DC-048 attach: licenseManager + userStore on app.locals
    if (ctx.licenseManager || userStore) {
      router.use('/auth', (req, _res, next) => {
        if (ctx.licenseManager) req.app.locals.licenseManager = ctx.licenseManager;
        if (userStore) req.app.locals.userStore = userStore;
        next();
      });
    }

    router.use('/auth', adminRouter);
  }

  return router;
};
