// ═══════════════════════════════════════════════════════════
// 📱 Example Express App with OpenTelemetry
// ═══════════════════════════════════════════════════════════
//
// Run with: npm run trace
// Or: node --require ./tracing.js app.js

const express = require('express');
const { trace, SpanStatusCode } = require('@opentelemetry/api');

const app = express();
const PORT = process.env.PORT || 3000;

// Get a tracer for manual instrumentation
const tracer = trace.getTracer('example-app');

// Simulated database
const users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' },
  { id: 3, name: 'Charlie', email: 'charlie@example.com' },
];

// Middleware to add request tracing
app.use((req, res, next) => {
  const span = trace.getActiveSpan();
  if (span) {
    span.setAttribute('http.user_agent', req.get('user-agent') || 'unknown');
  }
  next();
});

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', service: 'nodejs-example' });
});

// Get all users (with manual span)
app.get('/users', async (req, res) => {
  // Create a custom span for database operation
  const span = tracer.startSpan('db.query.users');
  span.setAttribute('db.system', 'memory');
  span.setAttribute('db.operation', 'SELECT');
  
  try {
    // Simulate database latency
    await sleep(Math.random() * 100);
    
    span.setAttribute('db.row_count', users.length);
    span.setStatus({ code: SpanStatusCode.OK });
    
    res.json({ users });
  } catch (error) {
    span.setStatus({
      code: SpanStatusCode.ERROR,
      message: error.message,
    });
    span.recordException(error);
    res.status(500).json({ error: error.message });
  } finally {
    span.end();
  }
});

// Get user by ID
app.get('/users/:id', async (req, res) => {
  const userId = parseInt(req.params.id);
  
  const span = tracer.startSpan('db.query.user_by_id');
  span.setAttribute('db.system', 'memory');
  span.setAttribute('db.operation', 'SELECT');
  span.setAttribute('user.id', userId);
  
  try {
    await sleep(Math.random() * 50);
    
    const user = users.find(u => u.id === userId);
    
    if (!user) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: 'User not found' });
      res.status(404).json({ error: 'User not found' });
    } else {
      span.setStatus({ code: SpanStatusCode.OK });
      res.json({ user });
    }
  } finally {
    span.end();
  }
});

// Create order (simulates complex operation with nested spans)
app.post('/orders', express.json(), async (req, res) => {
  const parentSpan = tracer.startSpan('order.create');
  
  try {
    // Step 1: Validate inventory
    const inventorySpan = tracer.startSpan('inventory.check', {
      attributes: { 'order.items': req.body.items?.length || 0 },
    });
    await sleep(Math.random() * 100);
    inventorySpan.setStatus({ code: SpanStatusCode.OK });
    inventorySpan.end();
    
    // Step 2: Process payment
    const paymentSpan = tracer.startSpan('payment.process', {
      attributes: { 'payment.method': 'credit_card' },
    });
    await sleep(Math.random() * 200);
    paymentSpan.setStatus({ code: SpanStatusCode.OK });
    paymentSpan.end();
    
    // Step 3: Create order record
    const createSpan = tracer.startSpan('db.insert.order');
    await sleep(Math.random() * 50);
    const orderId = Date.now().toString(36);
    createSpan.setAttribute('order.id', orderId);
    createSpan.setStatus({ code: SpanStatusCode.OK });
    createSpan.end();
    
    parentSpan.setStatus({ code: SpanStatusCode.OK });
    res.json({ orderId, status: 'created' });
    
  } catch (error) {
    parentSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
    parentSpan.recordException(error);
    res.status(500).json({ error: error.message });
  } finally {
    parentSpan.end();
  }
});

// Simulate error endpoint (for testing error traces)
app.get('/error', (req, res) => {
  const span = trace.getActiveSpan();
  const error = new Error('Simulated error for testing');
  
  if (span) {
    span.recordException(error);
    span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
  }
  
  res.status(500).json({ error: error.message });
});

// Helper
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Start server
app.listen(PORT, () => {
  console.log(`🚀 Example app listening on http://localhost:${PORT}`);
  console.log('');
  console.log('Try these endpoints:');
  console.log(`  GET  http://localhost:${PORT}/health`);
  console.log(`  GET  http://localhost:${PORT}/users`);
  console.log(`  GET  http://localhost:${PORT}/users/1`);
  console.log(`  POST http://localhost:${PORT}/orders`);
  console.log(`  GET  http://localhost:${PORT}/error`);
});
