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

/**
 * Load test for hot public menu endpoints.
 *
 * Usage:
 *   k6 run load-tests/hot-endpoints.js
 *   k6 run -e BASE_URL=http://localhost:2000 -e RESTAURANT_SLUG=zhivan load-tests/hot-endpoints.js
 *   k6 run --scenario concurrent_burst load-tests/hot-endpoints.js
 *   k6 run --scenario sustained_load -e VUS=100 load-tests/hot-endpoints.js
 *
 * Scenarios:
 *   smoke            - quick sanity check (default when TEST_PROFILE=smoke)
 *   concurrent_burst - many VUs hit all 3 endpoints at once (reproduces hang)
 *   sustained_load   - ramp up and hold steady traffic
 *   thundering_herd  - same slug, no think time, maximum cache-miss pressure
 */

const BASE_URL = __ENV.BASE_URL || 'http://localhost:2000';
const SLUG = __ENV.RESTAURANT_SLUG || 'zhivan';
const REQUEST_TIMEOUT = __ENV.REQUEST_TIMEOUT || '30s';

const restaurantLatency = new Trend('restaurant_latency', true);
const foodsLatency = new Trend('foods_latency', true);
const categoriesLatency = new Trend('categories_latency', true);
const menuPageLatency = new Trend('menu_page_latency', true);
const errors = new Rate('errors');

const ENDPOINTS = {
  restaurant: `/public/restaurants/${SLUG}`,
  foods: `/public/foods/restaurant/${SLUG}`,
  categories: `/public/categories/restaurant/${SLUG}`,
};

const defaultVus = Number(__ENV.VUS || 50);
const defaultDuration = __ENV.DURATION || '1m';

const scenarios = {
  smoke: {
    executor: 'shared-iterations',
    vus: 2,
    iterations: 6,
    maxDuration: '1m',
    tags: { profile: 'smoke' },
  },
  concurrent_burst: {
    executor: 'shared-iterations',
    vus: defaultVus,
    iterations: Number(__ENV.ITERATIONS || defaultVus * 10),
    maxDuration: '5m',
    tags: { profile: 'concurrent_burst' },
  },
  sustained_load: {
    executor: 'ramping-vus',
    startVUs: 0,
    stages: [
      { duration: '30s', target: Math.floor(defaultVus / 2) },
      { duration: defaultDuration, target: defaultVus },
      { duration: '30s', target: 0 },
    ],
    gracefulRampDown: '10s',
    tags: { profile: 'sustained_load' },
  },
  thundering_herd: {
    executor: 'constant-vus',
    vus: defaultVus,
    duration: defaultDuration,
    tags: { profile: 'thundering_herd' },
  },
};

const profile = __ENV.TEST_PROFILE;
const activeScenarios =
  profile && scenarios[profile]
    ? { [profile]: scenarios[profile] }
    : {
        smoke: scenarios.smoke,
        concurrent_burst: scenarios.concurrent_burst,
        sustained_load: scenarios.sustained_load,
      };

export const options = {
  scenarios: activeScenarios,
  thresholds: {
    http_req_duration: ['p(95)<5000', 'p(99)<10000'],
    http_req_failed: ['rate<0.1'],
    errors: ['rate<0.1'],
    restaurant_latency: ['p(95)<3000'],
    foods_latency: ['p(95)<5000'],
    categories_latency: ['p(95)<3000'],
    menu_page_latency: ['p(95)<8000'],
  },
  summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(90)', 'p(95)', 'p(99)'],
};

function headers() {
  return { 'X-Slug': SLUG, Accept: 'application/json' };
}

function parseBody(res) {
  try {
    return JSON.parse(res.body);
  } catch {
    return null;
  }
}

function assertSuccess(res, name) {
  const body = parseBody(res);
  return check(res, {
    [`${name} status 200`]: (r) => r.status === 200,
    [`${name} responds in time`]: (r) => r.timings.duration < 10000,
    [`${name} success true`]: () => body?.success === true,
    [`${name} has data`]: () => body?.data !== undefined && body?.data !== null,
  });
}

function getEndpoint(path, metric, tag) {
  const res = http.get(`${BASE_URL}${path}`, {
    headers: headers(),
    tags: { endpoint: tag },
    timeout: REQUEST_TIMEOUT,
  });

  metric.add(res.timings.duration);
  const ok = assertSuccess(res, tag);
  errors.add(!ok);
  return res;
}

/** Simulates a real menu page: all 3 endpoints requested in parallel. */
function loadMenuPage() {
  const started = Date.now();

  const responses = http.batch([
    ['GET', `${BASE_URL}${ENDPOINTS.restaurant}`, null, { headers: headers(), tags: { endpoint: 'restaurant' }, timeout: REQUEST_TIMEOUT }],
    ['GET', `${BASE_URL}${ENDPOINTS.foods}`, null, { headers: headers(), tags: { endpoint: 'foods' }, timeout: REQUEST_TIMEOUT }],
    ['GET', `${BASE_URL}${ENDPOINTS.categories}`, null, { headers: headers(), tags: { endpoint: 'categories' }, timeout: REQUEST_TIMEOUT }],
  ]);

  restaurantLatency.add(responses[0].timings.duration);
  foodsLatency.add(responses[1].timings.duration);
  categoriesLatency.add(responses[2].timings.duration);
  menuPageLatency.add(Date.now() - started);

  const ok =
    assertSuccess(responses[0], 'restaurant') &&
    assertSuccess(responses[1], 'foods') &&
    assertSuccess(responses[2], 'categories');

  errors.add(!ok);
}

export function setup() {
  const res = http.get(`${BASE_URL}${ENDPOINTS.restaurant}`, {
    headers: headers(),
    timeout: REQUEST_TIMEOUT,
  });

  if (res.status !== 200) {
    throw new Error(
      `Setup failed: ${BASE_URL}${ENDPOINTS.restaurant} returned ${res.status}. ` +
        `Is the API running? Try: npm run start:dev`,
    );
  }

  return { baseUrl: BASE_URL, slug: SLUG };
}

export default function () {
  const profileTag = __ENV.TEST_PROFILE || 'mixed';

  if (profileTag === 'thundering_herd') {
    group('thundering_herd_single_endpoint', () => {
      getEndpoint(ENDPOINTS.foods, foodsLatency, 'foods');
      getEndpoint(ENDPOINTS.categories, categoriesLatency, 'categories');
      getEndpoint(ENDPOINTS.restaurant, restaurantLatency, 'restaurant');
    });
    return;
  }

  group('menu_page_parallel', loadMenuPage);
}

export function handleSummary(data) {
  const p95 = (name) => data.metrics[name]?.values?.['p(95)']?.toFixed(2) ?? 'n/a';
  const failed = data.metrics.http_req_failed?.values?.rate ?? 0;

  console.log('\n--- Hot endpoints load test summary ---');
  console.log(`Target: ${BASE_URL} | slug: ${SLUG}`);
  console.log(`Failed requests: ${(failed * 100).toFixed(2)}%`);
  console.log(`Restaurant p95: ${p95('restaurant_latency')} ms`);
  console.log(`Foods       p95: ${p95('foods_latency')} ms`);
  console.log(`Categories  p95: ${p95('categories_latency')} ms`);
  console.log(`Menu page   p95: ${p95('menu_page_latency')} ms`);
  console.log('---------------------------------------\n');

  return {
    stdout: JSON.stringify(data, null, 2),
    'load-tests/results/hot-endpoints-summary.json': JSON.stringify(data, null, 2),
  };
}
