/**
 * Integration tests for OIDC Token Exchange endpoint.
 * Uses a real Postgres test DB and Redis test instance.
 *
 * Routes covered:
 *   POST /api/v1/oidc/token — exchange a GitHub OIDC JWT for a SentryAgent.ai access token
 *
 * This is an unauthenticated endpoint — the GitHub OIDC JWT is the credential.
 * Trust-policy enforcement requires a matching oidc_trust_policies record.
 */

import crypto from 'crypto';
import request from 'supertest';
import { Application } from 'express';
import { v4 as uuidv4 } from 'uuid';
import { Pool } from 'pg';

const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 2048,
  publicKeyEncoding: { type: 'spki', format: 'pem' },
  privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

process.env['DATABASE_URL'] =
  process.env['TEST_DATABASE_URL'] ??
  'postgresql://sentryagent:sentryagent@localhost:5432/sentryagent_idp_test';
process.env['REDIS_URL'] = process.env['TEST_REDIS_URL'] ?? 'redis://localhost:6379/1';
process.env['JWT_PRIVATE_KEY'] = privateKey;
process.env['JWT_PUBLIC_KEY'] = publicKey;
process.env['NODE_ENV'] = 'test';
process.env['DEFAULT_ORG_ID'] = 'org_system';

import { createApp } from '../../src/app';
import { closePool } from '../../src/db/pool';
import { closeRedisClient } from '../../src/cache/redis';

describe('OIDC Token Exchange Endpoint Integration Tests', () => {
  let app: Application;
  let pool: Pool;

  beforeAll(async () => {
    app = await createApp();
    pool = new Pool({ connectionString: process.env['DATABASE_URL'] });

    await pool.query(`
      CREATE TABLE IF NOT EXISTS oidc_trust_policies (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        provider VARCHAR(20) NOT NULL,
        repository VARCHAR(255) NOT NULL,
        branch VARCHAR(100),
        agent_id VARCHAR(40) NOT NULL,
        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
      )
    `);
  });

  afterAll(async () => {
    await pool.end();
    await closePool();
    await closeRedisClient();
  });

  // ─── POST /oidc/token ────────────────────────────────────────────────────────

  describe('POST /api/v1/oidc/token', () => {
    it('should return 400 when request body is missing', async () => {
      const res = await request(app)
        .post('/api/v1/oidc/token')
        .send({});

      expect([400, 422]).toContain(res.status);
    });

    it('should return 400 when provider is missing', async () => {
      const res = await request(app)
        .post('/api/v1/oidc/token')
        .send({ token: 'fake-jwt', agentId: uuidv4() });

      expect([400, 422]).toContain(res.status);
    });

    it('should return 400 when token is missing', async () => {
      const res = await request(app)
        .post('/api/v1/oidc/token')
        .send({ provider: 'github', agentId: uuidv4() });

      expect([400, 422]).toContain(res.status);
    });

    it('should return 401 or 403 for an invalid GitHub OIDC token', async () => {
      // A malformed JWT will fail verification — the endpoint should reject it
      const res = await request(app)
        .post('/api/v1/oidc/token')
        .send({
          provider: 'github',
          token: 'eyJhbGciOiJSUzI1NiJ9.invalid.payload',
          agentId: uuidv4(),
          scope: 'agents:read',
        });

      expect([400, 401, 403, 422]).toContain(res.status);
    });

    it('should return 403 when no trust policy matches the repository', async () => {
      // Build a minimally valid JWT structure with github claims (but won't pass GitHub JWKS)
      // The endpoint will reject after trust-policy lookup fails
      const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');
      const claims = {
        iss: 'https://token.actions.githubusercontent.com',
        sub: 'repo:nonexistent/repo:ref:refs/heads/main',
        repository: 'nonexistent/repo',
        ref: 'refs/heads/main',
        aud: 'sentryagent.ai',
      };
      const payload = Buffer.from(JSON.stringify(claims)).toString('base64url');
      const fakeJwt = `${header}.${payload}.fakesig`;

      const res = await request(app)
        .post('/api/v1/oidc/token')
        .send({
          provider: 'github',
          token: fakeJwt,
          agentId: uuidv4(),
          scope: 'agents:read',
        });

      // Either 401 (invalid JWT signature) or 403 (trust policy violation)
      expect([400, 401, 403]).toContain(res.status);
    });
  });
});
