#!/usr/bin/env node
/**
 * Real-time Audio Stream Listener
 * Continuously captures audio and analyzes for specific triggers
 * 
 * Usage: node stream-listener.js [--trigger "word to detect"]
 */

const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');

const GEMINI_KEY = 'AIzaSyClMlVU3Z1jh1UBxTRn25yesH8RU1q_umY';
const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_KEY}`;

const CHUNK_DURATION = 3; // seconds per chunk
const CAPTURES_DIR = '/Users/jakeshore/.clawdbot/workspace/audio-captcha/captures';

// Parse args
const args = process.argv.slice(2);
const triggerIndex = args.indexOf('--trigger');
const triggerWord = triggerIndex >= 0 ? args[triggerIndex + 1] : null;

if (!fs.existsSync(CAPTURES_DIR)) {
  fs.mkdirSync(CAPTURES_DIR, { recursive: true });
}

async function analyzeChunk(audioPath, chunkNum) {
  const audioBuffer = fs.readFileSync(audioPath);
  const audioBase64 = audioBuffer.toString('base64');
  
  const prompt = triggerWord
    ? `Listen to this audio. Does it contain the word or phrase "${triggerWord}"? If yes, respond with "DETECTED: [what was said]". If no, respond with "NOTHING".`
    : `Transcribe this audio. If silence or noise only, say "SILENCE". Otherwise return what was said.`;
  
  try {
    const response = await fetch(GEMINI_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        contents: [{
          parts: [
            { text: prompt },
            { inline_data: { mime_type: 'audio/wav', data: audioBase64 } }
          ]
        }]
      })
    });
    
    const data = await response.json();
    const result = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
    
    return result.trim();
  } catch (err) {
    return `ERROR: ${err.message}`;
  }
}

async function captureChunk(chunkNum) {
  const filename = path.join(CAPTURES_DIR, `chunk-${chunkNum.toString().padStart(4, '0')}.wav`);
  
  return new Promise((resolve, reject) => {
    const ffmpeg = spawn('ffmpeg', [
      '-y',
      '-f', 'avfoundation',
      '-i', ':BlackHole 2ch',
      '-t', CHUNK_DURATION.toString(),
      '-ar', '16000',
      '-ac', '1',
      filename
    ], { stdio: ['pipe', 'pipe', 'pipe'] });
    
    ffmpeg.on('close', (code) => {
      if (code === 0 && fs.existsSync(filename)) {
        resolve(filename);
      } else {
        reject(new Error(`FFmpeg exited with code ${code}`));
      }
    });
    
    ffmpeg.on('error', reject);
  });
}

async function main() {
  console.log('🎧 Stream Listener Started');
  console.log(`   Chunk duration: ${CHUNK_DURATION}s`);
  console.log(`   Trigger word: ${triggerWord || '(none - transcribing all)'}`);
  console.log('   Press Ctrl+C to stop\n');
  console.log('='.repeat(50));
  
  let chunkNum = 0;
  
  while (true) {
    chunkNum++;
    const timestamp = new Date().toLocaleTimeString();
    
    try {
      process.stdout.write(`\n[${timestamp}] Chunk #${chunkNum}: Recording...`);
      const audioFile = await captureChunk(chunkNum);
      
      process.stdout.write(' Analyzing...');
      const result = await analyzeChunk(audioFile, chunkNum);
      
      // Clean up chunk file
      fs.unlinkSync(audioFile);
      
      if (result.includes('SILENCE') || result.includes('NOTHING')) {
        process.stdout.write(' (silence)');
      } else if (result.includes('DETECTED')) {
        console.log(`\n\n🚨 ${result}\n`);
      } else {
        console.log(`\n   → "${result}"`);
      }
      
    } catch (err) {
      console.log(` ERROR: ${err.message}`);
    }
  }
}

// Handle graceful shutdown
process.on('SIGINT', () => {
  console.log('\n\n👋 Listener stopped.');
  process.exit(0);
});

main().catch(console.error);
