/**
 * AuthProvider registry — composes every enabled provider for the current
 * DashCaddy instance.
 *
 * Today only `totp` exists. Adding a new provider:
 *   1. Create src/auth/providers/<name>.js exporting a class extending
 *      AuthProvider (see ./base.js for the contract).
 *   2. Add a case to `loadProvider()` below.
 *   3. Document in CHANGELOG.md. No route edits, no app.js edits.
 *
 * The registry returns a single object the auth router walks to:
 *   - list all enabled providers + their login methods (for the UI)
 *   - look up a provider by name (for /api/v1/auth/login/:provider/...)
 *   - look up the active provider for the check-session endpoint (every
 *     provider's session is the same DashCaddy cookie, so check-session
 *     is a global concern, not provider-specific)
 *
 * Sessions are global, not per-provider. Every successful verify() creates
 * the same DashCaddy session cookie. TOTP and email magic link both use
 * session.create() / setCookie() — there's no "TOTP session" vs "email session"
 * distinction. The check-session endpoint therefore doesn't dispatch to a
 * specific provider — it just verifies the cookie's validity (the existing
 * behavior in src/utilities/middleware.js isSessionValid()).
 */

const TotpProvider = require('./totp');
const EmailMagicLinkProvider = require('./email');

/**
 * Instantiate every enabled provider for this instance.
 *
 * @param {Object} deps  Shared infrastructure (see TotpProvider constructor)
 * @param {Object} config  The site config — providers.enabled.{name} toggles each.
 * @returns {Object}      { providers: Map<name, AuthProvider>, listEnabled() }
 */
function createAuthProviderRegistry(deps, config) {
  const providers = new Map();

  // Always load TOTP — it's the default. The legacy /api/v1/totp/* routes
  // and the Caddy forward_auth gate all assume TOTP exists.
  const totpProvider = new TotpProvider({
    ...deps,
    config: deps.config.totp, // the existing totpConfig object from app.js
    saveProviderConfig: deps.saveTotpConfig, // existing helper
    // DC-048: user store for bootstrap + audit attribution on TOTP logins.
    userStore: deps.userStore || null,
  });
  providers.set('totp', totpProvider);

  // DC-047: EmailMagicLinkProvider — second AuthProvider. Always register;
  // enablement is controlled by config.authProviders.email.enabled (defaults
  // to true so dev installs "just work"). Providers are constructed lazily
  // in the sense that even a provider with no SMTP config still issues and
  // verifies tokens — only delivery falls back to the dev console-log path.
  providers.set('email', new EmailMagicLinkProvider({
    ...deps,
    config: deps.config.email || { enabled: true, sessionDuration: '24h' },
    saveProviderConfig: deps.saveProviderConfig || (async () => {}),
    emailConfig: deps.emailConfig || null,
    siteConfig: deps.siteConfig || {},
    // DC-048: real authorization check via the user store. Without it,
    // every email is allowed (legacy single-user behavior).
    userStore: deps.userStore || null,
  }));

  // Future: OIDC, SAML, passkeys — each gated on config.authProviders
  // or its own toggle. The shape is uniform: { enabled: bool, ...rest }.
  //
  // if (config?.authProviders?.oidc?.enabled) {
  //   providers.set('oidc', new OidcProvider({
  //     ...deps, config: config.authProviders.oidc, saveProviderConfig: ...
  //   }));
  // }

  return {
    providers,
    /**
     * Return the list of enabled providers with their public config.
     * Used by GET /api/v1/auth/methods to drive the login UI.
     */
    async listEnabled() {
      const out = [];
      for (const [name, provider] of providers) {
        if (!(await provider.isEnabled())) continue;
        const methods = await provider.listMethods();
        out.push({
          name,
          config: await provider.getConfig(),
          methods,
        });
      }
      return out;
    },

    /**
     * All configured providers, including disabled-but-not-deleted ones.
     * For the settings UI.
     */
    async listAll() {
      const out = [];
      for (const [name, provider] of providers) {
        out.push({
          name,
          config: await provider.getConfig(),
          methods: await provider.listMethods(),
        });
      }
      return out;
    },

    /**
     * Look up a provider by name. Returns null if not registered.
     */
    getProvider(name) {
      return providers.get(name) || null;
    },
  };
}

module.exports = { createAuthProviderRegistry };