// src/server.js
const http = require('http');
const { Server } = require('socket.io');
const jwt = require('jsonwebtoken');

const app = require('./app');
const db = require('./db/knex');
const config = require('./config');

// mqtt exports { on, isConnected, shutdown, ... }
const mqttClient = require('./mqtt');

const { normalizeChargingStatus } = require('./domain/normalize/chargingStatus');
const { normalizeChargingConfig } = require('./domain/normalize/chargingConfig');

const server = http.createServer(app);

const io = new Server(server, {
  cors: {
    origin: config.corsOrigins,
    methods: ['GET', 'POST'],
    credentials: true,
  },
});

console.log('MQTT client exports=', Object.keys(mqttClient || {}));
if (typeof mqttClient?.on !== 'function') {
  console.error('[server] mqttClient.on não existe. Verifica src/mqtt/index.js');
}

// ---------------------------
// auth middleware do socket
// ---------------------------
io.use((socket, next) => {
  const token = socket.handshake.auth?.token;
  if (!token) return next(new Error('Authentication error: token required'));

  try {
    const payload = jwt.verify(token, config.jwtSecret);
    socket.user = payload;
    next();
  } catch (err) {
    next(new Error('Authentication error'));
  }
});

io.on('connection', (socket) => {
  console.log(`Client connected: ${socket.id}, user: ${socket.user.username}`);

  socket.on('joinChargers', async (chargerIds = []) => {
    try {
      if (!Array.isArray(chargerIds) || chargerIds.length === 0) return;

      const rows = await db('chargers')
        .whereIn('id', chargerIds)
        .andWhere({ user_id: socket.user.id })
        .select('id');

      const allowed = rows.map((r) => r.id);
      allowed.forEach((id) => socket.join(id));

      console.log(`Socket ${socket.id} joined chargers: ${allowed}`);
    } catch (err) {
      console.error('joinChargers error:', err);
    }
  });

  socket.on('disconnect', (reason) => {
    console.log(`Client disconnected: ${socket.id}, reason: ${reason}`);
  });
});

// ---------------------------
// Relay MQTT -> Socket.IO
// ---------------------------
if (typeof mqttClient?.on === 'function') {
  mqttClient.on('charging-status', (data) => {
    const normalized = normalizeChargingStatus(data);
    const chargerId = normalized.charger_id;
    if (!chargerId) return;

    io.to(chargerId).emit('charging-status', normalized);
  });

  mqttClient.on('charging-config', (data) => {
    const normalized = normalizeChargingConfig(data);
    const chargerId = normalized.charger_id;
    if (!chargerId) return;

    io.to(chargerId).emit('charging-config', normalized);
  });

  mqttClient.on('scheduler-state', (evt) => {
    const chargerId = evt?.charger_id;
    if (!chargerId) return;

    io.to(chargerId).emit('evse-scheduler', evt);
    io.to(chargerId).emit('scheduler-state', evt);
  });

  mqttClient.on('loadbalancing-state', (evt) => {
    const chargerId = evt?.charger_id;
    if (!chargerId) return;

    io.to(chargerId).emit('evse-loadbalancing', evt);
    io.to(chargerId).emit('loadbalancing-state', evt);
  });

  mqttClient.on('meter-live', (evt) => {
    const chargerId = evt?.charger_id;
    if (!chargerId) return;
    io.to(chargerId).emit('meter-live', evt);
  });

  mqttClient.on('auth-state', (evt) => {
    const chargerId = evt?.charger_id;
    if (!chargerId) return;
    io.to(chargerId).emit('auth-state', evt);
  });

  mqttClient.on('meters-config', (evt) => {
    const chargerId = evt?.charger_id;
    if (!chargerId) return;
    io.to(chargerId).emit('meters-config', evt);
  });
}

server.listen(config.port, () => {
  console.log(`Server listening on http://localhost:${config.port}`);
});

// ---------------------------
// Graceful shutdown
// ---------------------------
let shuttingDown = false;

async function shutdown(signal) {
  if (shuttingDown) return;
  shuttingDown = true;

  console.log(`[shutdown] recebido ${signal}. A encerrar...`);

  // para aceitar novos requests/sockets
  await new Promise((resolve) => {
    try {
      server.close(resolve);
    } catch {
      resolve();
    }
  });

  // fecha sockets
  await new Promise((resolve) => {
    try {
      io.close(() => resolve());
    } catch {
      resolve();
    }
  });

  // fecha mqtt
  try {
    if (typeof mqttClient?.shutdown === 'function') {
      await mqttClient.shutdown();
    }
  } catch (e) {
    console.error('[shutdown] mqtt shutdown error:', e?.message || e);
  }

  // fecha db
  try {
    await db.destroy();
  } catch (e) {
    console.error('[shutdown] db destroy error:', e?.message || e);
  }

  console.log('[shutdown] concluído.');
  process.exit(0);
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

process.on('unhandledRejection', (err) => {
  console.error('[unhandledRejection]', err);
  shutdown('unhandledRejection');
});

process.on('uncaughtException', (err) => {
  console.error('[uncaughtException]', err);
  shutdown('uncaughtException');
});
