/**
 * tapper — Main Load Test
 *
 * Simulates realistic user traffic against the platform:
 *   1. Register new users
 *   2. Login to obtain JWT
 *   3. Hit protected endpoints (profile, queue, matches, leaderboard)
 *
 * Ramp-up pattern:
 *   0 → 10 VUs  (30 s warm-up)
 *   10 → 50 VUs (30 s ramp)
 *   50 VUs      (1 m sustained)
 *   50 → 100 VUs(30 s ramp)
 *   100 VUs     (1 m sustained)
 *   100 → 0 VUs (30 s cool-down)
 *
 * Run:  k6 run loadtest/script.js
 * With env:  k6 run -e BASE_URL=http://myhost:3000 loadtest/script.js
 */

import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Counter, Rate, Trend } from 'k6/metrics';
import {
  registerAndLogin,
  authGet,
  authPost,
  getBaseUrl,
} from './helpers/auth.js';

// ── Custom metrics ──────────────────────────────────────────────────────────
const registerDuration = new Trend('custom_register_duration', true);
const loginDuration = new Trend('custom_login_duration', true);
const profileDuration = new Trend('custom_profile_duration', true);
const leaderboardDuration = new Trend('custom_leaderboard_duration', true);
const matchesDuration = new Trend('custom_matches_duration', true);
const queueJoinDuration = new Trend('custom_queue_join_duration', true);
const queueStatusDuration = new Trend('custom_queue_status_duration', true);
const errorCount = new Counter('custom_errors');
const successRate = new Rate('custom_success_rate');

// ── k6 options ──────────────────────────────────────────────────────────────
export const options = {
  scenarios: {
    main_load: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '30s', target: 10 },   // warm-up
        { duration: '30s', target: 50 },   // ramp to 50
        { duration: '1m', target: 50 },    // sustained 50
        { duration: '30s', target: 100 },  // ramp to 100
        { duration: '1m', target: 100 },   // sustained 100
        { duration: '30s', target: 0 },    // cool-down
      ],
      gracefulRampDown: '10s',
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<500'],         // 95th percentile under 500 ms
    http_req_failed: ['rate<0.01'],           // less than 1 % errors
    custom_register_duration: ['p(95)<800'],  // registration may be slower (bcrypt)
    custom_login_duration: ['p(95)<600'],
    custom_profile_duration: ['p(95)<300'],
    custom_leaderboard_duration: ['p(95)<400'],
    custom_success_rate: ['rate>0.99'],
  },
};

// ── Setup ───────────────────────────────────────────────────────────────────
export function setup() {
  // Verify the target is reachable
  const BASE_URL = getBaseUrl();
  const healthRes = http.get(`${BASE_URL}/api/health`);
  check(healthRes, {
    'setup: health endpoint reachable': (r) => r.status === 200,
  });
  if (healthRes.status !== 200) {
    console.error(`Health check failed (${healthRes.status}). Aborting.`);
    return { abort: true };
  }
  return { baseUrl: BASE_URL };
}

// ── Default (main VU function) ──────────────────────────────────────────────
export default function (data) {
  if (data && data.abort) {
    return;
  }

  const BASE_URL = getBaseUrl();

  // ── 1. Register & login ─────────────────────────────────────────────────
  let token = null;

  group('auth: register and login', () => {
    const start = Date.now();
    const result = registerAndLogin();
    registerDuration.add(Date.now() - start);
    token = result.token;

    if (!token) {
      errorCount.add(1);
      successRate.add(false);
      return;
    }
    successRate.add(true);
  });

  if (!token) {
    sleep(1);
    return; // can't continue without auth
  }

  sleep(0.5); // realistic think-time

  // ── 2. View own profile ─────────────────────────────────────────────────
  group('profile: get own', () => {
    const res = authGet('/api/profile', token, { name: 'GET /api/profile' });
    profileDuration.add(res.timings.duration);

    const ok = check(res, {
      'profile: status 200': (r) => r.status === 200,
      'profile: has username': (r) => {
        try { return r.json('username') !== undefined; } catch (_) { return false; }
      },
    });
    successRate.add(ok);
    if (!ok) errorCount.add(1);
  });

  sleep(0.3);

  // ── 3. View leaderboard ─────────────────────────────────────────────────
  group('leaderboard: top players', () => {
    const res = authGet('/api/leaderboard', token, { name: 'GET /api/leaderboard' });
    leaderboardDuration.add(res.timings.duration);

    const ok = check(res, {
      'leaderboard: status 200': (r) => r.status === 200,
    });
    successRate.add(ok);
    if (!ok) errorCount.add(1);
  });

  sleep(0.3);

  // ── 4. View match history ───────────────────────────────────────────────
  group('matches: own history', () => {
    const res = authGet('/api/matches', token, { name: 'GET /api/matches' });
    matchesDuration.add(res.timings.duration);

    const ok = check(res, {
      'matches: status 200': (r) => r.status === 200,
    });
    successRate.add(ok);
    if (!ok) errorCount.add(1);
  });

  sleep(0.3);

  // ── 5. Queue join / status / leave cycle ────────────────────────────────
  group('queue: join-poll-leave', () => {
    // Join queue
    const joinRes = authPost('/api/queue/join', {}, token, { name: 'POST /api/queue/join' });
    queueJoinDuration.add(joinRes.timings.duration);

    const joinOk = check(joinRes, {
      'queue join: status 200 or 201 or 409': (r) =>
        r.status === 200 || r.status === 201 || r.status === 409,
    });
    successRate.add(joinOk);
    if (!joinOk) errorCount.add(1);

    sleep(0.5);

    // Poll queue status
    const statusRes = authGet('/api/queue/status', token, { name: 'GET /api/queue/status' });
    queueStatusDuration.add(statusRes.timings.duration);

    const statusOk = check(statusRes, {
      'queue status: status 200': (r) => r.status === 200,
    });
    successRate.add(statusOk);
    if (!statusOk) errorCount.add(1);

    sleep(0.3);

    // Leave queue (cleanup)
    const leaveRes = authPost('/api/queue/leave', {}, token, { name: 'POST /api/queue/leave' });
    check(leaveRes, {
      'queue leave: status 200 or 204': (r) => r.status === 200 || r.status === 204,
    });
  });

  sleep(1); // think-time between iterations
}

// ── Teardown ────────────────────────────────────────────────────────────────
export function teardown(data) {
  console.log('Load test complete. Review thresholds above for pass/fail.');
}
