/**
 * Smoke tests for the caddy-upstreams router.
 *
 * No jest.mock('fs') here — the route module needs a real express
 * context to load, and the watcher logic is tested separately in
 * caddy-upstream-watcher.test.js.
 */

const express = require('express');

describe('routes/caddy-upstreams', () => {
  test('router builds with all expected paths and handlers', () => {
    const mod = require('../../routes/caddy-upstreams');
    const fakeWatcher = {
      snapshot: jest.fn(() => ({ upstreams: [], config: {} }))
    };
    const fakeHealthChecker = { incidents: [] };

    const router = mod({
      asyncHandler: (fn) => fn,
      caddyUpstreamWatcher: fakeWatcher,
      healthChecker: fakeHealthChecker
    });

    expect(router).toBeDefined();
    expect(typeof router.use).toBe('function');

    const paths = router.stack
      .filter((l) => l.route)
      .map((l) => Object.keys(l.route.methods).map((m) => `${m.toUpperCase()} ${l.route.path}`))
      .flat();

    expect(paths).toEqual(expect.arrayContaining([
      'GET /caddy/upstreams',
      'GET /caddy/upstreams/incidents',
      'POST /caddy/upstreams/mute',
      'POST /caddy/upstreams/:host/mute',
      'POST /caddy/upstreams/:host/unmute'
    ]));
  });

  test('GET /caddy/upstreams responds with watcher snapshot', async () => {
    const mod = require('../../routes/caddy-upstreams');
    const fakeSnapshot = { upstreams: [{ host: '1.1.1.1:80', status: 'up', muted: false }], config: {} };
    const fakeWatcher = { snapshot: jest.fn(() => fakeSnapshot) };
    const fakeHealthChecker = { incidents: [] };

    // Build a tiny express app with the route + a shim success/error responder.
    const app = express();
    app.use(express.json());
    app.use((req, res, next) => {
      res.success = (data) => res.json({ success: true, ...data });
      res.errorResponse = (msg, code) => res.status(code || 500).json({ success: false, error: msg });
      next();
    });
    app.use(mod({
      asyncHandler: (fn, _ctx) => async (req, res, next) => {
        try { await fn(req, res, next); } catch (e) { next(e); }
      },
      caddyUpstreamWatcher: fakeWatcher,
      healthChecker: fakeHealthChecker
    }));

    const server = app.listen(0);
    const { port } = server.address();
    const res = await fetch(`http://127.0.0.1:${port}/caddy/upstreams`);
    const body = await res.json();
    server.close();
    expect(body.success).toBe(true);
    expect(body.upstreams).toEqual(fakeSnapshot.upstreams);
  });

  test('POST /caddy/upstreams/mute with body {host, muted:"false"} does NOT mute (string coercion)', async () => {
    // Regression: bare route previously used `muted !== false` which muted
    // when muted was a string 'false' (because 'false' !== false). Fix
    // requires explicit `muted === false` to unmute.
    const mod = require('../../routes/caddy-upstreams');
    const fakeSnapshot = { upstreams: [], config: {} };
    const fakeWatcher = {
      snapshot: jest.fn(() => fakeSnapshot),
      upstreams: new Map([['known:80', { host: 'known:80' }]]),
      setMuted: jest.fn(() => ({ host: 'known:80', muted: false }))
    };

    const app = express();
    app.use(express.json());
    app.use((req, res, next) => {
      res.success = (data) => res.json({ success: true, ...data });
      res.errorResponse = (msg, code) => res.status(code || 500).json({ success: false, error: msg });
      next();
    });
    app.use(mod({
      asyncHandler: (fn, _ctx) => async (req, res, next) => {
        try { await fn(req, res, next); } catch (e) { next(e); }
      },
      caddyUpstreamWatcher: fakeWatcher,
      healthChecker: { incidents: [] }
    }));
    // Error middleware MUST be registered AFTER routes so it actually catches.
    app.use((err, req, res, next) => {
      if (err && err.statusCode === 400) {
        return res.status(400).json({ success: false, error: err.message });
      }
      return res.status(err?.statusCode || 500).json({ success: false, error: err?.message || 'unknown' });
    });

    const server = app.listen(0);
    const { port } = server.address();

    // String 'false' should NOT mute (should unmute or pass through)
    const res = await fetch(`http://127.0.0.1:${port}/caddy/upstreams/mute`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ host: 'known:80', muted: 'false' })
    });
    const body = await res.json();
    expect(fakeWatcher.setMuted).toHaveBeenCalledWith('known:80', false);

    // Unknown host should 400
    fakeWatcher.setMuted.mockClear();
    const res2 = await fetch(`http://127.0.0.1:${port}/caddy/upstreams/mute`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ host: 'not-a-real-host:80' })
    });
    const body2 = await res2.json();
    server.close();
    expect(res2.status).toBe(400);
    expect(body2.error).toMatch(/not a known upstream/);
    expect(fakeWatcher.setMuted).not.toHaveBeenCalled();
  });
});