/**
 * APIS-062 — Dashboard API auth guard.
 *
 * `createDashboardApi` exposes worker idle/running status and node (GPU)
 * status. The inventory flagged "no explicit auth guard visible". On reading
 * the source, a router-level guard DOES exist:
 *
 *   r.use((req,res,next) => {
 *     if (!authActive && !getUser(req)) req.user = { id:'local', role:'user' };
 *     if (!getUser(req)) { res.status(401).json({error:'Unauthenticated'}); return; }
 *     next();
 *   });
 *
 * So:
 *   - authActive=true + NO req.user           → 401 (authentication IS enforced)
 *   - authActive=true + any authenticated user → 200
 *   - authActive=false + NO req.user           → 200 (synthetic 'local' user)
 *
 * The existing dashboard-api.test.ts always injects a user and asserts 0 authz
 * cases; this file asserts the guard end-to-end (the unauthenticated path).
 *
 * Authorization model (confirmed intended, 2026-06-25): the guard is
 * authentication-only by design. Worker / GPU-node topology is information any
 * logged-in user may see, so there is intentionally NO requireAdmin gate. The
 * test below pins that contract: an authenticated non-admin user gets 200.
 */
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import express from 'express';
import request from 'supertest';
import { mkdtempSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { Repository } from '../db/repository.js';
import { createDashboardApi } from './dashboard-api.js';

function makeApp(opts: {
  user?: { id: string; role: 'admin' | 'user' } | null;
  authActive?: boolean;
  repo: Repository;
}): express.Application {
  const app = express();
  app.use(express.json());
  // Only inject a user when one is supplied; null/undefined => unauthenticated.
  if (opts.user) {
    app.use((req, _res, next) => {
      (req as any).user = opts.user;
      next();
    });
  }
  app.use(
    '/api/local/dashboard',
    createDashboardApi({
      repo: opts.repo,
      getWorkers: () => [{ id: 'w1', endpoint: 'http://x/v1', roles: ['task'] }],
      authActive: opts.authActive ?? true,
      backendStatusRegistry: null,
    }),
  );
  return app;
}

describe('APIS-062 dashboard-api auth guard', () => {
  let tmpDir = '';
  let repo: Repository;

  beforeEach(() => {
    tmpDir = mkdtempSync(join(tmpdir(), 'maestro-dashboard-guard-'));
    repo = new Repository(join(tmpDir, 'test.db'));
  });

  afterEach(() => {
    repo.close();
    rmSync(tmpDir, { recursive: true, force: true });
  });

  it('authActive=true + unauthenticated → 401 on /workers (guard enforced)', async () => {
    const app = makeApp({ user: null, authActive: true, repo });
    const res = await request(app).get('/api/local/dashboard/workers');
    expect(res.status).toBe(401);
    expect(res.body).toEqual({ error: 'Unauthenticated' });
  });

  it('authActive=true + authenticated user → 200 on /workers', async () => {
    const app = makeApp({ user: { id: 'u1', role: 'user' }, authActive: true, repo });
    const res = await request(app).get('/api/local/dashboard/workers');
    expect(res.status).toBe(200);
    expect(Array.isArray(res.body.workers)).toBe(true);
  });

  it('authActive=false + unauthenticated → 200 (synthetic local user, by design)', async () => {
    const app = makeApp({ user: null, authActive: false, repo });
    const res = await request(app).get('/api/local/dashboard/workers');
    expect(res.status).toBe(200);
  });

  it('node-status route is also behind the auth guard (401 when unauthenticated)', async () => {
    const app = makeApp({ user: null, authActive: true, repo });
    const res = await request(app).get('/api/local/dashboard/node-status');
    // Guard fires before the route, so 401 (not the 503 "registry not configured").
    expect(res.status).toBe(401);
  });

  // By design: worker/GPU-node topology is visible to ALL authenticated users
  // (not admin-only). Confirmed intended 2026-06-25 — there is deliberately no
  // requireAdmin gate; this pins the contract so a future accidental tightening
  // (or loosening to unauthenticated) is caught.
  it('authActive=true + non-admin authenticated user → 200 on /workers (topology is all-users, by design)', async () => {
    const app = makeApp({ user: { id: 'plain', role: 'user' }, authActive: true, repo });
    const res = await request(app).get('/api/local/dashboard/workers');
    expect(res.status).toBe(200);
    expect(Array.isArray(res.body.workers)).toBe(true);
  });
});
