/**
 * Characterization test pinning the TOP-LEVEL Express layer registration
 * order of createCoreServer. Express dispatches in registration order, so
 * extracting inline route blocks out of server.ts must keep this fingerprint
 * byte-identical — any drift means the refactor changed observable routing
 * behavior, not just structure.
 *
 * Two boots are pinned:
 *   1. minimal no-auth boot ({ repo, worktreeDir }) — the same shape
 *      server-subsystems.test.ts uses.
 *   2. auth-active boot (local auth) + ConfigManager — exercises the auth
 *      middleware pipeline, /api/* guards, setup/config mounts and the
 *      gateway mount/health/status ordering.
 */
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import type { Application } from 'express';
import { Repository } from '../db/repository.js';
import { ConfigManager } from '../config-manager.js';
import { createCoreServer } from './server.js';
import type { AuthConfig } from '../config.js';

interface Layer {
  route?: { path: string; methods: Record<string, boolean> };
  name?: string;
  regexp?: RegExp;
}

/** Ordered fingerprint of the app's top-level middleware/route layers. */
function fingerprint(app: Application): string[] {
  const stack: Layer[] = (app as unknown as { _router?: { stack: Layer[] } })._router?.stack ?? [];
  return stack.map((layer) => {
    if (layer.route) {
      const methods = Object.keys(layer.route.methods).sort().join(',');
      return `ROUTE ${methods} ${layer.route.path}`;
    }
    return `USE name=${layer.name ?? ''} path=${layer.regexp?.source ?? ''}`;
  });
}

function mkAuthConfig(): AuthConfig {
  return {
    sessionSecret: 'test-secret',
    sessionMaxAge: 3600_000,
    secureCookie: false,
    adminEmails: [],
    providers: {},
    local: { enabled: true, allowSignup: false },
  } as AuthConfig;
}

describe('createCoreServer route registration order', () => {
  let tempDir: string;
  let repo: Repository;

  beforeEach(() => {
    tempDir = mkdtempSync(join(tmpdir(), 'route-order-'));
    repo = new Repository(join(tempDir, 'db.sqlite'));
  });

  afterEach(() => {
    delete process.env.MCP_ENCRYPTION_KEY;
    repo.close();
    rmSync(tempDir, { recursive: true, force: true });
  });

  it('pins the no-auth minimal boot layer order', () => {
    const { app } = createCoreServer({ repo, worktreeDir: join(tempDir, 'wt') });
    expect(fingerprint(app)).toMatchSnapshot();
  });

  it('pins the auth-active + ConfigManager boot layer order', () => {
    const configPath = join(tempDir, 'config.yaml');
    writeFileSync(configPath, 'provider:\n  workers: []\n');
    const configManager = new ConfigManager(configPath);
    const { app, authActive } = createCoreServer({
      repo,
      worktreeDir: join(tempDir, 'wt'),
      authConfig: mkAuthConfig(),
      configManager,
      listenPort: 9876,
    });
    expect(authActive).toBe(true);
    expect(fingerprint(app)).toMatchSnapshot();
  });
});
