import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Hono } from 'hono';
import { createManager } from '../src/plugin-manager.js';

// Tiny sub-app that answers GET / with a fixed text.
function mk(text) { const a = new Hono(); a.get('/', (c) => c.text(text)); return a; }

function fixture() {
  const state = { booted: false, published: null, previewed: null };
  const demo = {
    name: 'demo',
    routes: [
      // sub-app routes
      { path: '/demo-pub', app: mk('pub'), public: true },
      { path: '/demo-priv', app: mk('priv') },
      { path: '/demo-adm', app: mk('adm'), admin: true },
      // handler routes (method + handler)
      { method: 'get', path: '/h-pub', handler: (c) => c.text('hpub'), public: true },
      { method: 'post', path: '/h-priv', handler: (c) => c.text('hpriv') },
      // per-route middleware (e.g. a rate limit)
      {
        method: 'post', path: '/h-limited', public: true,
        use: [async (c, next) => (c.req.header('x-pass') ? next() : c.json({ e: 'limited' }, 429))],
        handler: (c) => c.text('ok'),
      },
    ],
    onBoot: async () => { state.booted = true; },
    onPublish: async (_ctx, p) => { state.published = p.path; },
    onPreview: async (_ctx, p) => { state.previewed = p.path; },
    stats: async () => ({ widgets: 3 }),
    migrations: ['001_demo.sql'],
  };
  return { demo, state };
}

// Build the same pipeline as index.js: public → requireAuth → private(+admin).
function buildApp(m) {
  const app = new Hono();
  m.mountPublic(app);
  app.use('/api/*', async (c, next) => (c.req.header('x-auth') ? next() : c.json({ error: 'no auth' }, 401)));
  const requireAdmin = async (c, next) => (c.req.header('x-admin') ? next() : c.json({ error: 'no admin' }, 403));
  m.mountPrivate(app, '/api', requireAdmin);
  return app;
}

test('manager: names / has', () => {
  const { demo } = fixture();
  const m = createManager([demo]);
  assert.deepEqual(m.names(), ['demo']);
  assert.equal(m.has('demo'), true);
  assert.equal(m.has('nope'), false);
});

test('manager: public routes reachable WITHOUT auth (sub-app + handler)', async () => {
  const app = buildApp(createManager([fixture().demo]));
  const sub = await app.request('/api/demo-pub');
  assert.equal(sub.status, 200); assert.equal(await sub.text(), 'pub');
  const h = await app.request('/api/h-pub');
  assert.equal(h.status, 200); assert.equal(await h.text(), 'hpub');
});

test('manager: private routes require auth (sub-app + handler)', async () => {
  const app = buildApp(createManager([fixture().demo]));
  assert.equal((await app.request('/api/demo-priv')).status, 401);
  assert.equal((await app.request('/api/h-priv', { method: 'POST' })).status, 401);
  const ok = await app.request('/api/h-priv', { method: 'POST', headers: { 'x-auth': '1' } });
  assert.equal(ok.status, 200); assert.equal(await ok.text(), 'hpriv');
});

test('manager: admin route guarded by requireAdmin', async () => {
  const app = buildApp(createManager([fixture().demo]));
  assert.equal((await app.request('/api/demo-adm', { headers: { 'x-auth': '1' } })).status, 403);
  const ok = await app.request('/api/demo-adm', { headers: { 'x-auth': '1', 'x-admin': '1' } });
  assert.equal(ok.status, 200); assert.equal(await ok.text(), 'adm');
});

test('manager: per-route use middleware runs (rate-limit style)', async () => {
  const app = buildApp(createManager([fixture().demo]));
  assert.equal((await app.request('/api/h-limited', { method: 'POST' })).status, 429);
  const ok = await app.request('/api/h-limited', { method: 'POST', headers: { 'x-pass': '1' } });
  assert.equal(ok.status, 200); assert.equal(await ok.text(), 'ok');
});

test('manager: lifecycle hooks run', async () => {
  const { demo, state } = fixture();
  const m = createManager([demo]);
  await m.runBoot({});
  await m.runPublish({}, { path: 'a.md' });
  await m.runPreview({}, { path: 'b.md' });
  assert.equal(state.booted, true);
  assert.equal(state.published, 'a.md');
  assert.equal(state.previewed, 'b.md');
});

test('manager: stats merged under plugin name', async () => {
  const m = createManager([fixture().demo]);
  assert.deepEqual(await m.collectStats({}), { demo: { widgets: 3 } });
});

test('manager: declared migrations surfaced', () => {
  const m = createManager([fixture().demo]);
  assert.deepEqual(m.migrations(), [{ plugin: 'demo', file: '001_demo.sql', url: null }]);
});

test('manager: empty plugin set is a no-op', async () => {
  const m = createManager([]);
  assert.deepEqual(m.names(), []);
  assert.deepEqual(await m.collectStats({}), {});
  assert.deepEqual(m.migrations(), []);
  const app = buildApp(m);
  assert.equal((await app.request('/api/anything', { headers: { 'x-auth': '1' } })).status, 404);
});
