// Integration: /api/internal/prefetch/tank01 (Session 18).
//
// Verifies (a) the endpoint is gated by the shared internal key,
// (b) the request body is translated into the prefetch module's
// argv shape correctly, and (c) the module's summary is returned
// verbatim. The prefetch module itself is mocked — it has its own
// unit coverage in tests/unit/tank01Prefetch.test.js.

const express = require('express');
const request = require('supertest');

jest.mock('../../scripts/tank01-prefetch', () => ({
  main: jest.fn(),
}));
const tank01Prefetch = require('../../scripts/tank01-prefetch');

jest.mock('../../src/services/quotaTracker', () => ({
  getAllQuotaStatuses: jest.fn(),
}));
const quotaTracker = require('../../src/services/quotaTracker');

beforeEach(() => {
  jest.resetAllMocks();
  process.env.VYNDR_INTERNAL_KEY = 'test-internal-key-9999';
});

function mountApp() {
  // Reset the require cache so the route picks up the mocked
  // tank01-prefetch module fresh between tests.
  delete require.cache[require.resolve('../../src/routes/internal')];
  const internalRoutes = require('../../src/routes/internal');
  const app = express();
  app.use(express.json());
  app.use('/api/internal', internalRoutes);
  return app;
}

describe('POST /api/internal/prefetch/tank01', () => {
  test('rejects requests without the internal key', async () => {
    const app = mountApp();
    const res = await request(app)
      .post('/api/internal/prefetch/tank01')
      .send({ dryRun: true });
    expect(res.status).toBe(401);
    expect(tank01Prefetch.main).not.toHaveBeenCalled();
  });

  test('translates body into argv and returns the summary', async () => {
    const fakeSummary = {
      nba: { games: 3, boxscores: 8, odds: true, skipped: null },
      mlb: { games: 5, boxscores: 12, bvp: 0, skipped: null, bvp_skipped_reason: null },
      requestsSpent: 4,
      dryRun: false,
    };
    tank01Prefetch.main.mockResolvedValueOnce(fakeSummary);

    const app = mountApp();
    const res = await request(app)
      .post('/api/internal/prefetch/tank01')
      .set('x-internal-key', 'test-internal-key-9999')
      .send({ max: 25, sports: ['nba', 'mlb'], dryRun: false });

    expect(res.status).toBe(200);
    expect(res.body).toEqual({ ok: true, summary: fakeSummary });
    expect(tank01Prefetch.main).toHaveBeenCalledTimes(1);
    const argv = tank01Prefetch.main.mock.calls[0][0];
    expect(argv).toContain('--max=25');
    expect(argv).toContain('--sports=nba,mlb');
    expect(argv).not.toContain('--dry-run');
  });

  test('forwards --dry-run when requested', async () => {
    tank01Prefetch.main.mockResolvedValueOnce({ dryRun: true });
    const app = mountApp();
    const res = await request(app)
      .post('/api/internal/prefetch/tank01')
      .set('x-internal-key', 'test-internal-key-9999')
      .send({ dryRun: true });
    expect(res.status).toBe(200);
    const argv = tank01Prefetch.main.mock.calls[0][0];
    expect(argv).toContain('--dry-run');
  });

  test('returns 500 with the underlying error when the module rejects', async () => {
    tank01Prefetch.main.mockRejectedValueOnce(new Error('budget exhausted'));
    const app = mountApp();
    const res = await request(app)
      .post('/api/internal/prefetch/tank01')
      .set('x-internal-key', 'test-internal-key-9999')
      .send({});
    expect(res.status).toBe(500);
    expect(res.body).toEqual({ ok: false, error: 'budget exhausted' });
  });

  test('accepts string sports (single sport) and forwards it', async () => {
    tank01Prefetch.main.mockResolvedValueOnce({ ok: true });
    const app = mountApp();
    await request(app)
      .post('/api/internal/prefetch/tank01')
      .set('x-internal-key', 'test-internal-key-9999')
      .send({ sports: 'mlb' });
    const argv = tank01Prefetch.main.mock.calls[0][0];
    expect(argv).toContain('--sports=mlb');
  });
});

describe('GET /api/internal/quota (Session 20)', () => {
  test('rejects without the internal key', async () => {
    const app = mountApp();
    const res = await request(app).get('/api/internal/quota');
    expect(res.status).toBe(401);
    expect(quotaTracker.getAllQuotaStatuses).not.toHaveBeenCalled();
  });

  test('returns the per-provider snapshot when keyed', async () => {
    const fakeSnapshot = [
      { provider: 'odds-api', name: 'The Odds API', used: 487, limit: 500, remaining: 13, pct: 0.974, allowed: false, period: '2026-06', quotaType: 'monthly' },
      { provider: 'tank01',   name: 'Tank01',       used: 30,  limit: 1000, remaining: 970, pct: 0.03, allowed: true, period: '2026-06', quotaType: 'monthly' },
    ];
    quotaTracker.getAllQuotaStatuses.mockResolvedValueOnce(fakeSnapshot);

    const app = mountApp();
    const res = await request(app)
      .get('/api/internal/quota')
      .set('x-internal-key', 'test-internal-key-9999');
    expect(res.status).toBe(200);
    expect(res.body).toEqual({ ok: true, providers: fakeSnapshot });
  });

  test('returns 500 with the underlying error when the tracker throws', async () => {
    quotaTracker.getAllQuotaStatuses.mockRejectedValueOnce(new Error('redis down'));
    const app = mountApp();
    const res = await request(app)
      .get('/api/internal/quota')
      .set('x-internal-key', 'test-internal-key-9999');
    expect(res.status).toBe(500);
    expect(res.body).toEqual({ ok: false, error: 'redis down' });
  });
});
