// ============================================================
// AUDIO BACKUPS ROUTES — Server-side audio backup storage
// Stores compressed audio in PostgreSQL, auto-deletes after 24h
// ============================================================

var express = require('express');
var router = express.Router();
var fs = require('fs');
var os = require('os');
var path = require('path');
var zlib = require('zlib');
var multer = require('multer');
var db = require('../db/database');
var { authMiddleware } = require('../middleware/auth');
var cryptoUtil = require('../utils/crypto');
var { serverError } = require('../utils/errors');

// 25MB upload limit (same as transcribe). DiskStorage so 10 concurrent
// uploads don't pin 250MB of RAM at once — multer streams to a temp
// file as bytes arrive, the route reads from req.file.path. Identical
// user-visible perf (the upload is bottlenecked on the wire either
// way), much flatter memory profile.
var TMP_DIR = path.join(os.tmpdir(), 'pedscribe-audio-uploads');
try { fs.mkdirSync(TMP_DIR, { recursive: true }); } catch (e) {}
var upload = multer({
  storage: multer.diskStorage({ destination: TMP_DIR }),
  limits: { fileSize: 25 * 1024 * 1024 },
});

router.use(authMiddleware);

// ── POST save audio backup (compressed) ──────────────────────────────────
router.post('/audio-backups', upload.single('audio'), async function(req, res) {
  console.log('[AudioBackup] POST received, hasFile=' + !!req.file + ', user=' + (req.user ? req.user.id : 'none'));
  // Always remove the temp file when this handler returns — success or
  // failure path. The Buffer-based version had no file to clean up;
  // diskStorage means we own the lifecycle.
  var tmpPath = req.file && req.file.path;
  function cleanup() {
    if (tmpPath) {
      fs.unlink(tmpPath, function(unlinkErr) {
        if (unlinkErr && unlinkErr.code !== 'ENOENT') console.warn('[AudioBackup] tmp cleanup failed:', unlinkErr.message);
      });
      tmpPath = null;
    }
  }
  try {
    if (!req.file) return res.status(400).json({ error: 'No audio file' });

    var module = req.body.module || 'encounter';
    var originalSize = req.file.size;

    // Read the temp file once into a Buffer — gzip needs it in one shot
    // anyway. For 25MB max this is a single, transient allocation per
    // request instead of a persistent 25MB Buffer pinned by multer.
    var raw = await fs.promises.readFile(tmpPath);

    // Gzip compress, then AES-256-GCM encrypt the audio data.
    var compressed = await new Promise(function(resolve, reject) {
      zlib.gzip(raw, { level: 6 }, function(err, result) {
        if (err) reject(err); else resolve(result);
      });
    });
    var stored = cryptoUtil.encryptBuffer(compressed);

    var result = await db.run(
      'INSERT INTO audio_backups (user_id, module, mime_type, size_bytes, compressed_bytes, audio_data) VALUES ($1,$2,$3,$4,$5,$6)',
      [req.user.id, module, req.file.mimetype || 'audio/webm', originalSize, stored.length, stored]
    );

    var ratio = originalSize > 0 ? Math.round((1 - compressed.length / originalSize) * 100) : 0;
    console.log('[AudioBackup] Saved ' + (originalSize / 1024).toFixed(0) + 'KB → ' + (compressed.length / 1024).toFixed(0) + 'KB (' + ratio + '% compression) id=' + result.lastInsertRowid + ' user=' + req.user.id);

    res.json({ success: true, id: result.lastInsertRowid, originalSize: originalSize, compressedSize: compressed.length });
  } catch (e) {
    return serverError(res, 'AudioBackup save', e, 'Could not save audio backup');
  } finally {
    cleanup();
  }
});

// ── GET list audio backups ───────────────────────────────────────────────
router.get('/audio-backups', async function(req, res) {
  try {
    var rows = await db.all(
      "SELECT id, module, mime_type, size_bytes, compressed_bytes, created_at, expires_at FROM audio_backups WHERE user_id = $1 AND expires_at > NOW() ORDER BY created_at DESC",
      [req.user.id]
    );
    res.json({ success: true, backups: rows });
  } catch (e) { return serverError(res, 'AudioBackup list', e, 'Could not list backups'); }
});

// ── GET download audio backup (decompressed) ─────────────────────────────
router.get('/audio-backups/:id/audio', async function(req, res) {
  try {
    var row = await db.get(
      'SELECT audio_data, mime_type, size_bytes FROM audio_backups WHERE id = $1 AND user_id = $2 AND expires_at > NOW()',
      [req.params.id, req.user.id]
    );
    if (!row) return res.status(404).json({ error: 'Backup not found or expired' });

    // Decrypt (if encrypted row) then gunzip. Legacy rows are passed through.
    var ciphertext = row.audio_data;
    var gzipped = cryptoUtil.isEncryptedBuffer(ciphertext) ? cryptoUtil.decryptBuffer(ciphertext) : ciphertext;
    var decompressed = await new Promise(function(resolve, reject) {
      zlib.gunzip(gzipped, function(err, result) {
        if (err) reject(err); else resolve(result);
      });
    });

    res.setHeader('Content-Type', row.mime_type || 'audio/webm');
    res.setHeader('Content-Length', decompressed.length);
    res.send(decompressed);
  } catch (e) { return serverError(res, 'AudioBackup read', e, 'Could not retrieve audio backup'); }
});

// ── DELETE audio backup ──────────────────────────────────────────────────
router.delete('/audio-backups/:id', async function(req, res) {
  try {
    var result = await db.run(
      'DELETE FROM audio_backups WHERE id = $1 AND user_id = $2',
      [req.params.id, req.user.id]
    );
    if (result.changes === 0) return res.status(404).json({ error: 'Not found' });
    res.json({ success: true });
  } catch (e) { return serverError(res, 'AudioBackup delete', e, 'Could not delete backup'); }
});

module.exports = router;
