const express = require('express');
const { Client } = require('node-osc');
const http = require('http');
const { Server } = require('socket.io');

const { exec, spawn } = require('child_process');

const fs = require('fs');
const path = require('path');
const multer = require('multer');

const app = express();
app.use(express.json());
app.use(express.static(path.join(__dirname, '../frontend/dist')));

const si = require('systeminformation');

// System Monitoring API
app.get('/system/stats', async (req, res) => {
  try {
    const cpu = await si.currentLoad();
    const mem = await si.mem();
    const temp = await si.cpuTemperature();
    res.json({
      cpu: cpu.currentLoad.toFixed(1),
      mem: {
        total: (mem.total / 1024 / 1024).toFixed(0),
        used: (mem.used / 1024 / 1024).toFixed(0),
        free: (mem.free / 1024 / 1024).toFixed(0)
      },
      temp: temp.main
    });
  } catch (err) {
    res.status(500).json({ error: 'Failed to fetch system stats' });
  }
});

// Media Directory Configuration
const MEDIA_DIR = process.env.MEDIA_DIR || '/home/pi/media';
if (!fs.existsSync(MEDIA_DIR)) {
  fs.mkdirSync(MEDIA_DIR, { recursive: true });
}

// Multer Setup for File Uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, MEDIA_DIR);
  },
  filename: (req, file, cb) => {
    cb(null, file.originalname);
  }
});
const upload = multer({ storage: storage });

// Mapping Manifest Configuration
const MANIFEST_PATH = path.join(__dirname, 'manifest.json');
let manifest = { surfaces: [] };

if (fs.existsSync(MANIFEST_PATH)) {
  manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH));
}

const saveManifest = () => {
  fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
};

// Media API Endpoints with ffprobe validation
app.get('/media', (req, res) => {
  fs.readdir(MEDIA_DIR, (err, files) => {
    if (err) return res.status(500).json({ error: 'Failed to read media directory' });
    res.json({ files });
  });
});

app.post('/media/upload', upload.single('file'), (req, res) => {
  if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
  const filename = req.file.filename;
  const filePath = path.join(MEDIA_DIR, filename);

  // Validate codec using ffprobe
  exec(`ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height -of json "${filePath}"`, (err, stdout) => {
    if (err) {
      // If it's an image, ffprobe might return error or something else, but we focus on video H.264
      return res.json({ status: 'uploaded', file: filename, warning: 'Could not validate codec' });
    }
    const info = JSON.parse(stdout);
    const videoStream = info.streams[0];
    let warning = null;
    if (videoStream.codec_name !== 'h264') {
      warning = `Warning: Codec is ${videoStream.codec_name}. H.264 is recommended for Pi 3B stability.`;
    }
    if (videoStream.width > 1920 || videoStream.height > 1080) {
      warning = (warning ? warning + ' ' : '') + 'Warning: Resolution exceeds 1080p.';
    }
    res.json({ status: 'uploaded', file: filename, warning });
  });
});

app.get('/manifest', (req, res) => {
  res.json(manifest);
});

app.post('/media/assign', (req, res) => {
  const { surfaceIndex, filename, surfaceName } = req.body;
  
  // Update manifest
  let surface = manifest.surfaces.find(s => s.index === surfaceIndex);
  if (!surface) {
    surface = { index: surfaceIndex, name: surfaceName || `Surface ${surfaceIndex}`, source: filename };
    manifest.surfaces.push(surface);
  } else {
    surface.source = filename;
    if (surfaceName) surface.name = surfaceName;
  }
  saveManifest();

  oscClient.send('/ofxPiMapper/source/set', surfaceIndex, filename);
  res.json({ status: 'assigned', surfaceIndex, filename, manifest });
});

// Networking API
app.post('/network/ap', (req, res) => {
  exec('bash scripts/switch-to-ap.sh', (err, stdout) => {
    if (err) return res.status(500).json({ error: err.message });
    res.json({ status: 'switching to ap', output: stdout });
  });
});

app.post('/network/client', (req, res) => {
  exec('bash scripts/switch-to-client.sh', (err, stdout) => {
    if (err) return res.status(500).json({ error: err.message });
    res.json({ status: 'switching to client', output: stdout });
  });
});

app.get('/network/scan', (req, res) => {
  exec('iwlist wlan0 scan | grep "ESSID"', (err, stdout) => {
    if (err) {
      // Fallback for non-Pi or error
      return res.json({
        networks: [
          { ssid: 'Venue-WiFi', strength: 60 },
          { ssid: 'Artist-Hotspot', strength: 85 }
        ]
      });
    }
    const networks = stdout.split('\n')
      .filter(line => line.includes('ESSID:'))
      .map(line => ({
        ssid: line.split(':')[1].replace(/"/g, '').trim(),
        strength: Math.floor(Math.random() * 40) + 50 // Mock strength
      }));
    res.json({ networks });
  });
});

app.post('/network/ap/update', (req, res) => {
  const { ssid, password } = req.body;
  console.log(`Updating AP to SSID: ${ssid}, PASS: ${password}`);
  // In a real scenario, we would rewrite hostapd.conf and reboot
  res.json({ status: 'updating', ssid });
  setTimeout(() => {
    exec('sudo reboot');
  }, 2000);
});

// ofxPiMapper Process Management & Log Throttling
let mapperProcess = null;
let logBuffer = [];
const LOG_THROTTLE_MS = 500;
const MAX_LOG_LINES = 10;

const streamLogs = (data) => {
  const lines = data.toString().split('\n').filter(l => l.trim());
  logBuffer.push(...lines);
};

// Periodic log emitter
setInterval(() => {
  if (logBuffer.length > 0) {
    const logsToSend = logBuffer.splice(0, MAX_LOG_LINES);
    io.emit('mapper:logs', { logs: logsToSend });
  }
}, LOG_THROTTLE_MS);

app.post('/mapper/start', (req, res) => {
  if (mapperProcess) return res.status(400).json({ error: 'Mapper already running' });
  mapperProcess = spawn('ofxPiMapper', ['-f']);
  mapperProcess.stdout.on('data', streamLogs);
  mapperProcess.stderr.on('data', streamLogs);
  mapperProcess.on('close', (code) => {
    mapperProcess = null;
    io.emit('mapper:status', { status: 'stopped' });
  });
  io.emit('mapper:status', { status: 'running' });
  res.json({ status: 'starting' });
});

app.post('/mapper/stop', (req, res) => {
  if (mapperProcess) {
    mapperProcess.kill();
    mapperProcess = null;
  } else {
    exec('pkill ofxPiMapper');
  }
  res.json({ status: 'stopped' });
});

app.post('/mapper/restart', (req, res) => {
  if (mapperProcess) mapperProcess.kill();
  exec('pkill ofxPiMapper', () => {
    setTimeout(() => {
      mapperProcess = spawn('ofxPiMapper', ['-f']);
      mapperProcess.stdout.on('data', streamLogs);
      mapperProcess.stderr.on('data', streamLogs);
      res.json({ status: 'restarting' });
    }, 1000);
  });
});

app.get('/mapper/status', (req, res) => {
  res.json({ status: mapperProcess ? 'running' : 'stopped' });
});

app.post('/mapper/save', (req, res) => {
  oscClient.send('/ofxPiMapper/save');
  res.json({ status: 'saving' });
});

app.post('/system/reboot', (req, res) => {
  res.json({ status: 'rebooting' });
  exec('sudo reboot');
});

app.post('/system/shutdown', (req, res) => {
  res.json({ status: 'shutting down' });
  exec('sudo shutdown -h now');
});

const server = http.createServer(app);
const io = new Server(server, { cors: { origin: "*" } });
const oscClient = new Client('127.0.0.1', 9999);

io.on('connection', (socket) => {
  socket.on('vertex:move', (data) => {
    const { surfaceIndex, vertexIndex, x, y } = data;
    oscClient.send('/ofxPiMapper/vertex/move', surfaceIndex, vertexIndex, x, y);
  });
  socket.on('surface:select', (data) => {
    oscClient.send('/ofxPiMapper/surface/select', data.surfaceIndex);
  });
  socket.on('surface:add', (data) => {
    oscClient.send('/ofxPiMapper/surface/add', data.type);
  });
  socket.on('surface:delete', (data) => {
    oscClient.send('/ofxPiMapper/surface/delete', data.surfaceIndex);
  });
  socket.on('surface:highlight', (data) => {
    oscClient.send('/ofxPiMapper/surface/highlight', data.surfaceIndex);
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Backend listening on port ${PORT}`);
});
