import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';
import { randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';

// Load environment variables
const API_ORIGIN = __ENV.API_ORIGIN || 'https://api.lab.veza';
const STREAM_ORIGIN = __ENV.STREAM_ORIGIN || 'https://stream.lab.veza';
const TEST_EMAIL_PREFIX = __ENV.TEST_EMAIL_PREFIX || 'user+stream';
const TEST_EMAIL_DOMAIN = __ENV.TEST_EMAIL_DOMAIN || 'lab.veza';
const TEST_PASSWORD_PREFIX = __ENV.TEST_PASSWORD_PREFIX || 'V3za!stream-';

// Custom metrics
const streamHealthDuration = new Trend('stream_health_duration');
const streamUploadDuration = new Trend('stream_upload_duration');
const streamPlayDuration = new Trend('stream_play_duration');
const streamErrorRate = new Rate('stream_errors');
const bytesStreamed = Counter('bytes_streamed');

// Test configuration
export const options = {
  scenarios: {
    stream_load: {
      executor: 'constant-arrival-rate',
      rate: 20,           // 20 requests per second
      timeUnit: '1s',
      duration: '3m',
      preAllocatedVUs: 50,
      maxVUs: 100,
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<500', 'p(99)<1000'],
    stream_health_duration: ['p(95)<100', 'p(99)<200'],
    stream_play_duration: ['p(95)<300', 'p(99)<500'],
    stream_errors: ['rate<0.01'], // Less than 1% error rate
    http_req_failed: ['rate<0.01'],
  },
};

// Helper to create authenticated user
function createAuthenticatedUser() {
  const user = {
    email: `${TEST_EMAIL_PREFIX}${randomString(8)}@${TEST_EMAIL_DOMAIN}`,
    password: `${TEST_PASSWORD_PREFIX}${randomString(8)}`,
  };
  
  const registerRes = http.post(`${API_ORIGIN}/auth/register`, JSON.stringify(user), {
    headers: { 'Content-Type': 'application/json' },
  });
  
  if (registerRes.status !== 201) {
    return null;
  }
  
  try {
    const body = JSON.parse(registerRes.body);
    return {
      ...user,
      accessToken: body.access_token,
      userId: body.user.id,
    };
  } catch (e) {
    return null;
  }
}

// Generate simple audio data (WAV header + silence)
function generateSimpleAudioData(durationSeconds = 10) {
  const sampleRate = 44100;
  const numChannels = 2;
  const bitsPerSample = 16;
  const dataSize = sampleRate * numChannels * (bitsPerSample / 8) * durationSeconds;
  
  // Create WAV header
  const header = new ArrayBuffer(44);
  const view = new DataView(header);
  
  // RIFF chunk descriptor
  view.setUint32(0, 0x52494646, false); // "RIFF"
  view.setUint32(4, 36 + dataSize, true); // File size - 8
  view.setUint32(8, 0x57415645, false); // "WAVE"
  
  // fmt sub-chunk
  view.setUint32(12, 0x666d7420, false); // "fmt "
  view.setUint32(16, 16, true); // Subchunk size
  view.setUint16(20, 1, true); // Audio format (PCM)
  view.setUint16(22, numChannels, true);
  view.setUint32(24, sampleRate, true);
  view.setUint32(28, sampleRate * numChannels * (bitsPerSample / 8), true); // Byte rate
  view.setUint16(32, numChannels * (bitsPerSample / 8), true); // Block align
  view.setUint16(34, bitsPerSample, true);
  
  // data sub-chunk
  view.setUint32(36, 0x64617461, false); // "data"
  view.setUint32(40, dataSize, true);
  
  // Create silence data
  const silence = new ArrayBuffer(dataSize);
  
  // Combine header and data
  const audioBuffer = new Uint8Array(header.byteLength + silence.byteLength);
  audioBuffer.set(new Uint8Array(header), 0);
  audioBuffer.set(new Uint8Array(silence), header.byteLength);
  
  return audioBuffer.buffer;
}

// Setup: Create test users and sample audio
export function setup() {
  const users = [];
  const userCount = 30;
  
  console.log(`Creating ${userCount} test users for streaming test...`);
  
  for (let i = 0; i < userCount; i++) {
    const user = createAuthenticatedUser();
    if (user) {
      users.push(user);
    }
    sleep(0.1);
  }
  
  console.log(`Created ${users.length} authenticated users`);
  
  // Generate test audio data
  const audioData = generateSimpleAudioData(10); // 10 second audio
  
  return { users, audioData };
}

export default function (data) {
  const { users, audioData } = data;
  
  // Pick a random user
  const user = users[Math.floor(Math.random() * users.length)];
  if (!user) {
    console.error('No user available');
    return;
  }
  
  const headers = {
    'Authorization': `Bearer ${user.accessToken}`,
  };
  
  // Test 1: Stream health check
  const healthStart = new Date();
  const healthRes = http.get(`${STREAM_ORIGIN}/health`, {
    headers,
    tags: { name: 'stream_health' },
  });
  const healthEnd = new Date();
  
  streamHealthDuration.add(healthEnd - healthStart);
  
  check(healthRes, {
    'stream health status is 200': (r) => r.status === 200,
    'stream health response time < 100ms': (r) => r.timings.duration < 100,
  });
  
  if (healthRes.status !== 200) {
    streamErrorRate.add(1);
  }
  
  sleep(0.5);
  
  // Test 2: Upload audio (10% of requests)
  if (Math.random() < 0.1) {
    const formData = {
      audio: http.file(audioData, 'test-audio.wav', 'audio/wav'),
    };
    
    const uploadStart = new Date();
    const uploadRes = http.post(`${STREAM_ORIGIN}/upload`, formData, {
      headers: {
        ...headers,
      },
      tags: { name: 'stream_upload' },
    });
    const uploadEnd = new Date();
    
    streamUploadDuration.add(uploadEnd - uploadStart);
    
    check(uploadRes, {
      'upload status is 201': (r) => r.status === 201,
      'upload has stream_id': (r) => {
        try {
          const body = JSON.parse(r.body);
          return body.stream_id !== undefined;
        } catch {
          return false;
        }
      },
    });
    
    if (uploadRes.status !== 201) {
      streamErrorRate.add(1);
    }
  }
  
  sleep(0.5);
  
  // Test 3: Stream playback endpoint
  const playStart = new Date();
  const playRes = http.get(`${STREAM_ORIGIN}/play/demo`, {
    headers,
    tags: { name: 'stream_play' },
    responseType: 'none', // Don't store response body to save memory
  });
  const playEnd = new Date();
  
  streamPlayDuration.add(playEnd - playStart);
  
  check(playRes, {
    'play status is 200': (r) => r.status === 200,
    'play has audio content-type': (r) => {
      const contentType = r.headers['Content-Type'] || r.headers['content-type'];
      return contentType && contentType.includes('audio');
    },
    'play response time < 300ms': (r) => r.timings.duration < 300,
  });
  
  if (playRes.status === 200) {
    // Record bytes streamed
    const contentLength = parseInt(playRes.headers['Content-Length'] || '0');
    bytesStreamed.add(contentLength);
  } else {
    streamErrorRate.add(1);
  }
  
  sleep(1);
  
  // Test 4: Get stream metadata (50% of requests)
  if (Math.random() < 0.5) {
    const metadataRes = http.get(`${STREAM_ORIGIN}/metadata/demo`, {
      headers,
      tags: { name: 'stream_metadata' },
    });
    
    check(metadataRes, {
      'metadata status is 200': (r) => r.status === 200,
      'metadata has duration': (r) => {
        try {
          const body = JSON.parse(r.body);
          return body.duration !== undefined;
        } catch {
          return false;
        }
      },
    });
  }
  
  sleep(1);
  
  // Test 5: List available streams (20% of requests)
  if (Math.random() < 0.2) {
    const listRes = http.get(`${STREAM_ORIGIN}/streams`, {
      headers,
      tags: { name: 'stream_list' },
    });
    
    check(listRes, {
      'list status is 200': (r) => r.status === 200,
      'list returns array': (r) => {
        try {
          const body = JSON.parse(r.body);
          return Array.isArray(body.streams || body);
        } catch {
          return false;
        }
      },
    });
  }
  
  sleep(2); // Think time between iterations
}

// Teardown
export function teardown(data) {
  console.log('Streaming load test completed');
  console.log(`Total users created: ${data.users.length}`);
}