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

// ── Configuration ────────────────────────────────────────────────────────────
const BASE_URL = __ENV.VOICEPRINT_BASE_URL || 'http://localhost:3000';
const API_TOKEN = __ENV.API_TOKEN || 'test-token';
const DURATION = __ENV.DURATION || '300s'; // 5 minutes
const TARGET_RPS = parseInt(__ENV.TARGET_RPS || '500', 10);

// P99 latency thresholds (ms)
const THRESHOLDS = {
  enrollment: parseInt(__ENV.ENROLLMENT_P99_MS || '500', 10),
  verification: parseInt(__ENV.VERIFICATION_P99_MS || '250', 10),
  modelRetrieval: parseInt(__ENV.MODEL_RETRIEVAL_P99_MS || '100', 10),
};

// ── Custom Metrics ───────────────────────────────────────────────────────────
const enrollmentLatency = new Trend('enrollment_p99');
const verificationLatency = new Trend('verification_p99');
const modelRetrievalLatency = new Trend('model_retrieval_p99');

const enrollmentSuccess = new Rate('enrollment_success');
const verificationSuccess = new Rate('verification_success');
const modelRetrievalSuccess = new Rate('model_retrieval_success');

// ── Helpers ──────────────────────────────────────────────────────────────────
function uuidv4() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    const v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

// Generate a realistic audio payload (base64-encoded WAV-like buffer)
// ~3 seconds of 16kHz mono 16-bit audio = ~96KB
function generateAudioPayload() {
  const size = 96000;
  const audio = new Array(size);
  for (let i = 0; i < size; i++) {
    audio[i] = Math.floor(Math.random() * 256);
  }
  return btoa(String.fromCharCode(...audio.slice(0, 2048)));
}

const headers = {
  'Content-Type': 'application/json',
  'Authorization': `Bearer ${API_TOKEN}`,
};

// ── Scenario: Enrollment (POST /voiceprint/enroll) ──────────────────────────
function testEnrollment() {
  const payload = JSON.stringify({
    name: `voice_profile_${uuidv4()}`,
    audio: generateAudioPayload(),
  });

  const res = http.post(`${BASE_URL}/voiceprint/enroll`, payload, { headers });
  const duration = res.timings.duration;
  enrollmentLatency.add(duration);

  const success = res.status === 201;
  enrollmentSuccess.add(success);

  check(res, {
    'enrollment: status 201': (r) => r.status === 201,
    'enrollment: has enrollment.id': (r) => {
      try {
        const json = JSON.parse(r.body);
        return !!json.enrollment && !!json.enrollment.id;
      } catch {
        return false;
      }
    },
    `enrollment: P99 < ${THRESHOLDS.enrollment}ms`: (r) => duration < THRESHOLDS.enrollment,
  });

  return res.json()?.enrollment?.id || uuidv4();
}

// ── Scenario: Verification (POST /voiceprint/analyze) ───────────────────────
function testVerification() {
  const payload = JSON.stringify({
    audio: generateAudioPayload(),
  });

  const res = http.post(`${BASE_URL}/voiceprint/analyze`, payload, { headers });
  const duration = res.timings.duration;
  verificationLatency.add(duration);

  const success = res.status === 201;
  verificationSuccess.add(success);

  check(res, {
    'verification: status 201': (r) => r.status === 201,
    'verification: has analysis.id': (r) => {
      try {
        const json = JSON.parse(r.body);
        return !!json.analysis && !!json.analysis.id;
      } catch {
        return false;
      }
    },
    `verification: P99 < ${THRESHOLDS.verification}ms`: (r) => duration < THRESHOLDS.verification,
  });

  return res.json()?.analysis?.id || uuidv4();
}

// ── Scenario: Model Retrieval (GET /voiceprint/results/:id) ─────────────────
function testModelRetrieval(modelId) {
  const id = modelId || uuidv4();
  const res = http.get(`${BASE_URL}/voiceprint/results/${id}`, { headers });
  const duration = res.timings.duration;
  modelRetrievalLatency.add(duration);

  // 200 = found, 404 = not found (both valid for load testing)
  const success = res.status === 200 || res.status === 404;
  modelRetrievalSuccess.add(success);

  check(res, {
    'model_retrieval: status 200 or 404': (r) => r.status === 200 || r.status === 404,
    `model_retrieval: P99 < ${THRESHOLDS.modelRetrieval}ms`: (r) => duration < THRESHOLDS.modelRetrieval,
  });
}

// ── Default Scenario: Weighted mixed workload ────────────────────────────────
export const options = {
  scenarios: {
    sustained_load: {
      executor: 'constant-arrival-rate',
      duration: DURATION,
      rate: TARGET_RPS,
      preAllocatedVUs: 20,
      maxVUs: 100,
      startTime: '0s',
      exec: 'mixedWorkload',
      tags: { scenario: 'sustained_load' },
    },
  },
  thresholds: {
    `enrollment_p99`: [`p(99)<${THRESHOLDS.enrollment}`],
    `verification_p99`: [`p(99)<${THRESHOLDS.verification}`],
    `model_retrieval_p99`: [`p(99)<${THRESHOLDS.modelRetrieval}`],
    `enrollment_success`: ['rate>0.95'],
    `verification_success`: ['rate>0.95'],
    `model_retrieval_success`: ['rate>0.95'],
    http_req_duration: [`p(95)<400`, `p(99)<500`],
    http_req_failed: ['rate<0.05'],
  },
};

// Mixed workload: 30% enrollment, 45% verification, 25% model retrieval
export function mixedWorkload() {
  const rand = Math.random();

  if (rand < 0.3) {
    const modelId = testEnrollment();
    sleep(0.1);
    testModelRetrieval(modelId);
  } else if (rand < 0.75) {
    const modelId = testVerification();
    sleep(0.05);
    testModelRetrieval(modelId);
  } else {
    testModelRetrieval();
  }

  sleep(0.05);
}

// ── Individual endpoint scenarios for targeted testing ───────────────────────
export const endpointScenarios = {
  enrollment_only: {
    executor: 'constant-arrival-rate',
    duration: DURATION,
    rate: TARGET_RPS,
    preAllocatedVUs: 20,
    maxVUs: 100,
    exec: 'enrollmentOnly',
    startTime: '0s',
    tags: { scenario: 'enrollment_only' },
  },
  verification_only: {
    executor: 'constant-arrival-rate',
    duration: DURATION,
    rate: TARGET_RPS,
    preAllocatedVUs: 20,
    maxVUs: 100,
    exec: 'verificationOnly',
    startTime: '0s',
    tags: { scenario: 'verification_only' },
  },
  model_retrieval_only: {
    executor: 'constant-arrival-rate',
    duration: DURATION,
    rate: TARGET_RPS,
    preAllocatedVUs: 20,
    maxVUs: 100,
    exec: 'modelRetrievalOnly',
    startTime: '0s',
    tags: { scenario: 'model_retrieval_only' },
  },
};

export function enrollmentOnly() {
  testEnrollment();
  sleep(0.1);
}

export function verificationOnly() {
  testVerification();
  sleep(0.05);
}

export function modelRetrievalOnly() {
  testModelRetrieval();
  sleep(0.02);
}

// ── Summary Hook ─────────────────────────────────────────────────────────────
export function handleSummary(data) {
  return {
    'stdout': `\n=== Voiceprint Load Test Results ===\n`,
    'summary.json': JSON.stringify({
      timestamp: new Date().toISOString(),
      duration: DURATION,
      targetRPS: TARGET_RPS,
      thresholds: THRESHOLDS,
      metrics: {
        enrollment: {
          p99: data.metrics.enrollment_p99?.values['p(99)']?.toFixed(2) || 'N/A',
          p95: data.metrics.enrollment_p99?.values['p(95)']?.toFixed(2) || 'N/A',
          avg: data.metrics.enrollment_p99?.values.avg?.toFixed(2) || 'N/A',
          count: data.metrics.enrollment_p99?.values.count || 0,
          successRate: (data.metrics.enrollment_success?.values.rate || 0) * 100 + '%',
        },
        verification: {
          p99: data.metrics.verification_p99?.values['p(99)']?.toFixed(2) || 'N/A',
          p95: data.metrics.verification_p99?.values['p(95)']?.toFixed(2) || 'N/A',
          avg: data.metrics.verification_p99?.values.avg?.toFixed(2) || 'N/A',
          count: data.metrics.verification_p99?.values.count || 0,
          successRate: (data.metrics.verification_success?.values.rate || 0) * 100 + '%',
        },
        modelRetrieval: {
          p99: data.metrics.model_retrieval_p99?.values['p(99)']?.toFixed(2) || 'N/A',
          p95: data.metrics.model_retrieval_p99?.values['p(95)']?.toFixed(2) || 'N/A',
          avg: data.metrics.model_retrieval_p99?.values.avg?.toFixed(2) || 'N/A',
          count: data.metrics.model_retrieval_p99?.values.count || 0,
          successRate: (data.metrics.model_retrieval_success?.values.rate || 0) * 100 + '%',
        },
      },
      passed: Object.entries(data.metrics).every(
        ([_, metric]) => metric?.thresholds?.every?.((t) => t.pass)
      ),
    }, null, 2),
  };
}
