// =============================================================================
// K6 Load Test — PgBouncer Connection Pooling Validation
//
// This script validates that PgBouncer handles concurrent database connections
// correctly under production-like load. It targets the GoodGo API endpoints
// that trigger database queries (health, listings search, auth).
//
// Usage:
//   k6 run e2e/load/pgbouncer-pool-test.js
//   k6 run --env BASE_URL=http://localhost:3001 e2e/load/pgbouncer-pool-test.js
//
// Requirements:
//   - GoodGo API running (docker-compose.prod.yml or local dev)
//   - PgBouncer service running and healthy
//   - Seeded database (pnpm db:seed)
// =============================================================================

import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';

// ── Custom Metrics ───────────────────────────────────────────────────────────
const errorRate = new Rate('errors');
const dbQueryDuration = new Trend('db_query_duration', true);
const poolExhaustedErrors = new Counter('pool_exhausted_errors');

// ── Configuration ────────────────────────────────────────────────────────────
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3001';

export const options = {
  // Ramp-up pattern to stress-test connection pooling
  stages: [
    // Warm-up: gradually increase to 50 concurrent users
    { duration: '30s', target: 50 },
    // Sustained load: hold at 50 users
    { duration: '1m', target: 50 },
    // Spike: jump to 150 users (should exceed default_pool_size=20)
    { duration: '30s', target: 150 },
    // Peak: hold at 200 users (matches max_client_conn=200)
    { duration: '1m', target: 200 },
    // Cool-down: ramp back to 0
    { duration: '30s', target: 0 },
  ],

  thresholds: {
    // 95th percentile response time under 2 seconds
    http_req_duration: ['p(95)<2000'],
    // Error rate under 5%
    errors: ['rate<0.05'],
    // No pool-exhaustion errors
    pool_exhausted_errors: ['count<1'],
    // 99% of requests succeed
    http_req_failed: ['rate<0.01'],
  },
};

// ── Helper Functions ─────────────────────────────────────────────────────────

function getHeaders() {
  return {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  };
}

function checkResponse(res, name) {
  const success = check(res, {
    [`${name}: status 2xx`]: (r) => r.status >= 200 && r.status < 300,
    [`${name}: response time < 2s`]: (r) => r.timings.duration < 2000,
  });

  errorRate.add(!success);

  // Detect pool exhaustion (typically 503 or connection refused)
  if (res.status === 503 || res.status === 0) {
    poolExhaustedErrors.add(1);
  }

  if (res.timings.duration) {
    dbQueryDuration.add(res.timings.duration);
  }

  return success;
}

// ── Test Scenarios ───────────────────────────────────────────────────────────

export default function () {
  // Each VU runs a mix of API calls that hit the database

  group('Health Check (DB connectivity)', () => {
    const res = http.get(`${BASE_URL}/health`, { headers: getHeaders() });
    checkResponse(res, 'health');
  });

  group('Listings Search (read-heavy, geo queries)', () => {
    // Search listings — triggers PostGIS queries through the pool
    const searchParams = {
      page: 1,
      limit: 20,
    };
    const res = http.get(
      `${BASE_URL}/api/v1/listings?page=${searchParams.page}&limit=${searchParams.limit}`,
      { headers: getHeaders() },
    );
    checkResponse(res, 'listings-search');
  });

  group('Listings Detail (single record lookup)', () => {
    // Fetch a specific listing — simulates detail page views
    const res = http.get(`${BASE_URL}/api/v1/listings?page=1&limit=1`, {
      headers: getHeaders(),
    });
    checkResponse(res, 'listings-detail');
  });

  group('Concurrent Burst (pool stress)', () => {
    // Fire multiple requests in parallel to stress the connection pool
    const responses = http.batch([
      ['GET', `${BASE_URL}/health`, null, { headers: getHeaders() }],
      [
        'GET',
        `${BASE_URL}/api/v1/listings?page=1&limit=5`,
        null,
        { headers: getHeaders() },
      ],
      [
        'GET',
        `${BASE_URL}/api/v1/listings?page=2&limit=5`,
        null,
        { headers: getHeaders() },
      ],
    ]);

    responses.forEach((res, i) => {
      checkResponse(res, `burst-request-${i}`);
    });
  });

  // Brief pause between iterations to simulate realistic user behavior
  sleep(0.5 + Math.random() * 1.5);
}

// ── Lifecycle Hooks ──────────────────────────────────────────────────────────

export function setup() {
  // Verify the API is reachable before starting load test
  const res = http.get(`${BASE_URL}/health`);
  if (res.status !== 200) {
    throw new Error(
      `API is not healthy at ${BASE_URL}/health — got status ${res.status}. ` +
        'Ensure docker-compose.prod.yml services are running.',
    );
  }
  console.log(`API healthy at ${BASE_URL}. Starting PgBouncer pool load test...`);
  return { baseUrl: BASE_URL };
}

export function teardown(data) {
  console.log(`Load test complete against ${data.baseUrl}.`);
  console.log('Review k6 output for pool_exhausted_errors and db_query_duration metrics.');
}
