import fs from 'fs';
import path from 'path';
import https from 'https';
import express from 'express';
import dotenv from 'dotenv';
import { WebSocket } from 'ws';
import { EventEmitter } from 'events';

dotenv.config();

const app = express();
app.use(express.json());

const CERT_DIR = path.resolve('certs');
const KEY_PATH = path.join(CERT_DIR, 'localhost.key');
const CRT_PATH = path.join(CERT_DIR, 'localhost.crt');

function loadHttpsCredentials() {
  if (!fs.existsSync(KEY_PATH) || !fs.existsSync(CRT_PATH)) {
    console.error(`HTTPS-Zertifikate fehlen in ${CERT_DIR}. Bitte 'npm install' ausführen (Postinstall generiert Zertifikate).`);
    process.exit(1);
  }
  return { key: fs.readFileSync(KEY_PATH), cert: fs.readFileSync(CRT_PATH) };
}

const HTTPS_PORT = parseInt(process.env.HTTPS_PORT || '2033', 10);
const WSS_URL = process.env.WSS_URL || 'wss://localhost:2096';
const WSS_INSECURE_TLS = String(process.env.WSS_INSECURE_TLS || 'true').toLowerCase() === 'true';

// Nur bestimmte Kommandos erlauben (aus .env)
const allowedCommands = new Set(
  (process.env.ALLOWED_COMMANDS || 'HOME,STOP,STATUS,RESET,PING,GCODEMOTOR')
    .split(',')
    .map(s => s.trim())
    .filter(Boolean)
);

// Broadcaster für Server-Sent Events
const bus = new EventEmitter();

let wsDriver = null;
let wsState = {
  connected: false,
  lastError: null,
  reconnectAttempts: 0,
};

function logAndBroadcast(level, message, data) {
  const payload = { ts: new Date().toISOString(), level, message, data };
  // Konsole
  const line = `[${payload.ts}] [${level}] ${message}`;
  //console.log(line, data ? data : '');
  // SSE an Clients
  bus.emit('event', JSON.stringify(payload));
}

function connectWss() {
  if (wsDriver && (wsDriver.readyState === wsDriver.OPEN || wsDriver.readyState === wsDriver.CONNECTING)) {
    return;
  }

  const tlsOptions = { rejectUnauthorized: !WSS_INSECURE_TLS };
  logAndBroadcast('info', `Verbinde zu WSS: ${WSS_URL} (rejectUnauthorized=${tlsOptions.rejectUnauthorized})`);

  wsDriver = new WebSocket(WSS_URL, tlsOptions);

  wsDriver.on('open', () => {
    wsState.connected = true;
    wsState.lastError = null;
    wsState.reconnectAttempts = 0;
    logAndBroadcast('info', 'WSS Driver verbunden');

  });

  wsDriver.on('message', (data) => {
    let text = '';
    try { text = typeof data === 'string' ? data : data.toString('utf8'); } catch { text = '[binary data]'; }

    logAndBroadcast('msg', 'Eingang von WSS', { text });  
  });

  wsDriver.on('close', (code, reason) => {
    wsState.connected = false;
    logAndBroadcast('warn', `WSS getrennt (code=${code}, reason=${reason?.toString?.() || ''})`);
    scheduleReconnect();
  });

  wsDriver.on('error', (err) => {
    wsState.lastError = err?.message || String(err);
    logAndBroadcast('error', 'WSS Fehler', { error: wsState.lastError });
  });
}

function scheduleReconnect() {
  wsState.reconnectAttempts += 1;
  const delay = 10000; // 10s
  logAndBroadcast('info', `Reconnecting in ${Math.round(delay/1000)}s...`);
  setTimeout(connectWss, delay);
}

// HTTP API
app.get('/api/status', (req, res) => {
  wsDriver.send("M114");
  console.log("M114 gesendet, warte auf Antwort...");
  res.json({
    httpsPort: HTTPS_PORT,
    wssUrl: WSS_URL,
    connected: wsState.connected,
    wsDriver: wsDriver ? wsDriver.readyState : null,
    reconnectAttempts: wsState.reconnectAttempts,
    lastError: wsState.lastError,
    allowedCommands: Array.from(allowedCommands)
  });
});

app.post('/api/send', (req, res) => {
  const { cmd, payload } = req.body || {};
  if (!cmd || !allowedCommands.has(String(cmd).trim())) {
    return res.status(400).json({ ok: false, error: 'Ungültiges oder nicht erlaubtes Kommando', allowed: Array.from(allowedCommands) });
  }
  if (!wsDriver || wsDriver.readyState !== wsDriver.OPEN) {
    return res.status(503).json({ ok: false, error: 'WSS nicht verbunden' });
  }
  const msg = { type: String(cmd).trim(), payload: payload ?? null };

  if(msg.type==="STATUS"){
    wsDriver.send("M114");
    logAndBroadcast('tx', 'Sende STATUS (M114) an WSS');
    return res.json({ ok: true, sent: msg });
  }

  if(msg.type==="GCODEMOTOR"){
    if(typeof msg.payload !== 'string' || !msg.payload.trim()){
      return res.status(400).json({ ok: false, error: 'Ungültiger Payload für GCODEMOTOR. Erwartet: String mit G-Code Befehl.' });
    }

    wsDriver.send(msg.payload);
    console.log(`G-Code gesendet: ${msg.payload}`);
    /*
    msg.payload = msg.payload.trim();
    var arrayMsg = msg.payload.split(' ').filter(s => s.trim());
    if(arrayMsg.length === 0 || !['G0','G1','G28', 'M0', 'M1', 'M114'].includes(arrayMsg[0].toUpperCase())){
      return res.status(400).json({ ok: false, error: 'Ungültiger G-Code Befehl. Nur G0, G1 und G28 sind erlaubt.' });
    }
    if(arrayMsg[1].toUpperCase().startsWith('X')){
      wsDriver.send(`G0 ${arrayMsg[1].toUpperCase()} F1000`); // Schnelles Verfahren zu X-Position
      console.log(`G0 ${arrayMsg[1].toUpperCase()} F1000 gesendet`);
    }
    */
    return res.json({ ok: true, sent: msg.payload});
  }
  
  try {
    wsDriver.send(JSON.stringify(msg));
    logAndBroadcast('tx', 'Sende an WSS', msg);
    return res.json({ ok: true, sent: msg });
  } catch (err) {
    logAndBroadcast('error', 'Senden an WSS fehlgeschlagen', { error: err?.message || String(err) });
    return res.status(500).json({ ok: false, error: 'Senden fehlgeschlagen' });
  }
});

// SSE-Endpoint
app.get('/api/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders?.();

  const send = (data) => {
    res.write(`data: ${data}

`);
  };

  const listener = (data) => send(data);
  bus.on('event', listener);

  // Initialstatus schicken
  send(JSON.stringify({ ts: new Date().toISOString(), level: 'info', message: 'SSE verbunden' }));

  req.on('close', () => {
    bus.off('event', listener);
    res.end();
  });
});


//snapshot_video0_1775319258906_two_cam.csv
//snapshot_video0_1775319258906_two_cam_annotated.jpg

// Neuester Snapshot-Endpunkt
app.get('/api/latest-snapshot', (req, res) => {
  const snapshotsDir = path.join(path.resolve('public'), 'snapshots');
  fs.readdir(snapshotsDir, (err, files) => {
    if (err) {
      return res.status(500).json({ error: 'Fehler beim Lesen des Snapshots-Verzeichnisses' });
    }
    const csvFiles = files.filter(file => file.endsWith('.csv')).map(file => ({
      name: file,
      path: path.join(snapshotsDir, file),
      mtime: fs.statSync(path.join(snapshotsDir, file)).mtime
    })).sort((a, b) => b.mtime - a.mtime);

    if (csvFiles.length === 0) {
      return res.status(404).json({ error: 'Keine CSV-Dateien gefunden' });
    }
    const latestFile = csvFiles[0];
    const baseName = path.basename(latestFile.name, path.extname(latestFile.name));
    const imageFilename = `${baseName}_annotated.jpg`;
    const imagePath = path.join(snapshotsDir, imageFilename);
    const imatePath2 = imagePath.includes('video0') ? imagePath.replace('video0', 'video1') : imagePath.replace('video1', 'video0');
    

    fs.readFile(latestFile.path, 'utf8', (err, data) => {
      if (err) {
        return res.status(500).json({ error: 'Fehler beim Lesen der Datei' });
      }

      const response = {
        filename: latestFile.name,
        mtime: latestFile.mtime.toISOString(),
        content: data
      };

      // Lade beide Bilder
      fs.readFile(imagePath, { encoding: 'base64' }, (jpgErr, jpgBase64) => {
        if (!jpgErr && jpgBase64) {
          response.imageFile = {
            filename: imageFilename,
            mimeType: 'image/jpeg',
            contentBase64: jpgBase64
          };
        }

        fs.readFile(imatePath2, { encoding: 'base64' }, (jpgErr2, jpgBase642) => {
          if (!jpgErr2 && jpgBase642) {
            response.image2 = {
              filename: path.basename(imatePath2),
              mimeType: 'image/jpeg',
              contentBase64: jpgBase642
            };
          }
          res.json(response);
        });
      });
    });
  });
});

// Statisches Frontend
app.use('/', express.static(path.resolve('public')));

// HTTPS-Server starten
const creds = loadHttpsCredentials();
const server = https.createServer({
  key: creds.key,
  cert: creds.cert,
}, app);

server.listen(HTTPS_PORT, () => {
  logAndBroadcast('info', `HTTPS Server läuft auf https://localhost:${HTTPS_PORT}`);
  // Nach Start WSS verbinden
  connectWss();
});
