/**
 * Integration tests for Marketplace endpoints.
 * Uses a real Postgres test DB and Redis test instance.
 *
 * Routes covered:
 *   GET /api/v1/marketplace         — list public agents
 *   GET /api/v1/marketplace/:id     — get a specific public agent
 *
 * Marketplace endpoints are unauthenticated (public listing).
 */

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';

const ORG_ID = uuidv4();
const PUBLIC_AGENT_ID = uuidv4();
const PRIVATE_AGENT_ID = uuidv4();

describe('Marketplace Endpoints 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 organizations (
        organization_id VARCHAR(40) PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        plan VARCHAR(20) NOT NULL DEFAULT 'free',
        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
      )
    `);

    await pool.query(`
      CREATE TABLE IF NOT EXISTS agents (
        agent_id VARCHAR(40) PRIMARY KEY,
        organization_id VARCHAR(40) NOT NULL,
        email VARCHAR(255) NOT NULL UNIQUE,
        agent_type VARCHAR(50) NOT NULL,
        version VARCHAR(20) NOT NULL,
        capabilities TEXT[] NOT NULL DEFAULT '{}',
        owner VARCHAR(100) NOT NULL,
        deployment_env VARCHAR(50) NOT NULL,
        status VARCHAR(20) NOT NULL DEFAULT 'active',
        is_public BOOLEAN NOT NULL DEFAULT false,
        did TEXT,
        did_created_at TIMESTAMPTZ,
        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
      )
    `);

    await pool.query(
      `INSERT INTO organizations (organization_id, name) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
      [ORG_ID, 'Test Marketplace Org'],
    );

    // Insert a public agent
    await pool.query(
      `INSERT INTO agents
         (agent_id, organization_id, email, agent_type, version, capabilities, owner, deployment_env, status, is_public)
       VALUES ($1, $2, $3, 'screener', 'v1.0.0', '{resume:read}', 'test-team', 'production', 'active', true)
       ON CONFLICT DO NOTHING`,
      [PUBLIC_AGENT_ID, ORG_ID, `public-${PUBLIC_AGENT_ID}@test.com`],
    );

    // Insert a private agent
    await pool.query(
      `INSERT INTO agents
         (agent_id, organization_id, email, agent_type, version, capabilities, owner, deployment_env, status, is_public)
       VALUES ($1, $2, $3, 'screener', 'v1.0.0', '{resume:read}', 'test-team', 'production', 'active', false)
       ON CONFLICT DO NOTHING`,
      [PRIVATE_AGENT_ID, ORG_ID, `private-${PRIVATE_AGENT_ID}@test.com`],
    );
  });

  afterAll(async () => {
    await pool.query(`DELETE FROM agents WHERE organization_id = $1`, [ORG_ID]);
    await pool.query(`DELETE FROM organizations WHERE organization_id = $1`, [ORG_ID]);
    await pool.end();
    await closePool();
    await closeRedisClient();
  });

  // ─── GET /marketplace ────────────────────────────────────────────────────────

  describe('GET /api/v1/marketplace', () => {
    it('should return 200 with a list of public agents', async () => {
      const res = await request(app).get('/api/v1/marketplace');

      expect(res.status).toBe(200);
      const items: unknown[] = res.body.data ?? res.body;
      expect(Array.isArray(items)).toBe(true);
    });

    it('should not expose private agents in the listing', async () => {
      const res = await request(app).get('/api/v1/marketplace');

      expect(res.status).toBe(200);
      const items = (res.body.data ?? res.body) as Array<{ agentId: string }>;
      const privateIds = items.map((a) => a.agentId);
      expect(privateIds).not.toContain(PRIVATE_AGENT_ID);
    });

    it('should support pagination via ?page= and ?limit=', async () => {
      const res = await request(app).get('/api/v1/marketplace?page=1&limit=5');

      expect(res.status).toBe(200);
    });
  });

  // ─── GET /marketplace/:id ────────────────────────────────────────────────────

  describe('GET /api/v1/marketplace/:id', () => {
    it('should return 200 with the public agent card', async () => {
      const res = await request(app).get(`/api/v1/marketplace/${PUBLIC_AGENT_ID}`);

      expect(res.status).toBe(200);
      expect(res.body.agentId).toBe(PUBLIC_AGENT_ID);
    });

    it('should return 404 for a private agent', async () => {
      const res = await request(app).get(`/api/v1/marketplace/${PRIVATE_AGENT_ID}`);

      expect(res.status).toBe(404);
    });

    it('should return 404 for a non-existent agent', async () => {
      const res = await request(app).get(`/api/v1/marketplace/${uuidv4()}`);

      expect(res.status).toBe(404);
    });
  });
});
