// routes/chargers.js
const express = require('express');
const { body, param, validationResult } = require('express-validator');
const router = express.Router();
const verifyToken = require('../middleware/verifyToken');
const db = require('../db');
const crypto = require('crypto');
const axios = require('axios');
const mqttClient = require('../mqtt/client');

router.use(verifyToken);

function handleValidation(req, res, next) {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ success: false, errors: errors.array() });
  }
  next();
}

// throttling simples em memória (por charger)
const lastConfigUpdateAt = new Map();

// GET /api/chargers
router.get('/', async (req, res) => {
  const userId = req.user.id;
  try {
    const chargers = await db('chargers')
      .where({ user_id: userId })
      .select('*');
    res.json({ success: true, data: chargers });
  } catch (err) {
    console.error('Erro ao buscar carregadores:', err);
    res.status(500).json({ success: false, message: 'Erro no servidor' });
  }
});

// GET /api/chargers/:id
router.get(
  '/:id',
  [param('id').isUUID()],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const userId = req.user.id;

    try {
      const charger = await db('chargers')
        .where({ id, user_id: userId })
        .first();

      if (!charger) {
        return res
          .status(404)
          .json({ success: false, message: 'Carregador não encontrado' });
      }

      let config = null;
      try {
        config = await db('charger_configs')
          .where({ charger_id: charger.id })
          .first();
      } catch (cfgErr) {
        console.error('[GET charger] erro ao buscar charger_configs:', cfgErr);
      }

      if (!config)
        config = {
          charger_id: charger.id,
          max_charging_current: 32,
          require_auth: false,
          rcm_enabled: false,
          temperature_limit: 60,
        };

      const numericFields = [
        'power_l1',
        'power_l2',
        'power_l3',
        'voltage_l1',
        'voltage_l2',
        'voltage_l3',
        'current_l1',
        'current_l2',
        'current_l3',
        'charging_current',
        'consumption',
      ];

      numericFields.forEach((field) => {
        const v = charger[field];
        charger[field] =
          v === null || v === undefined || v === '' ? 0 : Number(v);
        if (Number.isNaN(charger[field])) charger[field] = 0;
      });

      return res.json({ success: true, data: { ...charger, config } });
    } catch (err) {
      console.error('Erro ao buscar carregador:', err);

      return res.status(500).json({
        success: false,
        message:
          process.env.NODE_ENV === 'development'
            ? err.message || 'Erro no servidor'
            : 'Erro no servidor',
      });
    }
  }
);

// POST /api/chargers
router.post(
  '/',
  [
    body('location')
      .exists()
      .withMessage('O campo location é obrigatório')
      .isString()
      .withMessage('Location deve ser uma string')
      .isLength({ min: 1 })
      .withMessage('Location não pode estar vazio')
      .trim(),
  ],
  handleValidation,
  async (req, res) => {
    const { location } = req.body;
    const userId = req.user.id;
    const now = new Date().toISOString();

    let chargerID;
    do {
      chargerID = crypto.randomBytes(6).toString('hex');
    } while (
      await db('chargers').where({ mqtt_topic: chargerID }).first()
    );

    const mqtt_topic = chargerID;
    const mqtt_user = chargerID;
    const mqtt_pass = crypto.randomBytes(6).toString('hex');

    try {
      const [charger] = await db('chargers')
        .insert({
          user_id: userId,
          location,
          status: 'offline',
          charging_current: 0,
          charging_time: 0,
          consumption: 0,
          power_l1: 0.0,
          power_l2: 0.0,
          power_l3: 0.0,
          voltage_l1: 0.0,
          voltage_l2: 0.0,
          voltage_l3: 0.0,
          current_l1: 0.0,
          current_l2: 0.0,
          current_l3: 0.0,
          mqtt_user,
          mqtt_pass,
          mqtt_topic,
          updated_at: now,
        })
        .returning('*');

      await db('charger_configs').insert({
        charger_id: charger.id,
        max_charging_current: 32,
        require_auth: false,
        rcm_enabled: false,
        temperature_limit: 60,
        config_received_at: now,
      });

      try {
        await axios.post(
          'http://localhost:7000/client/create',
          {
            client_name: charger.mqtt_user,
            chargeID: charger.mqtt_topic,
            password: charger.mqtt_pass,
          },
          { timeout: 5000 }
        );
      } catch (err) {
        console.error(
          'Erro ao criar cliente Mosquitto:',
          err?.response?.data || err.message
        );
      }

      res.status(201).json({ success: true, data: charger });
    } catch (err) {
      console.error('Erro ao criar carregador:', err);
      res.status(500).json({ success: false, message: 'Erro no servidor' });
    }
  }
);

// PUT /api/chargers/:id
router.put(
  '/:id',
  [
    param('id').isUUID(),
    body('charger').optional().isObject(),
    body('config').optional().isObject(),
    body('location').optional().isString(), // compat front
  ],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const userId = req.user.id;

    let { charger = {}, config = {} } = req.body;

    // ✅ compatibilidade com front antigo: { location }
    if (req.body.location && !charger.location) {
      charger.location = req.body.location;
    }

    const trx = await db.transaction();

    const ALLOWED_CHARGER_FIELDS = ['location'];
    const safeChargerUpdate = Object.fromEntries(
      Object.entries(charger).filter(([k]) =>
        ALLOWED_CHARGER_FIELDS.includes(k)
      )
    );

    try {
      let updatedCharger;

      if (Object.keys(safeChargerUpdate).length > 0) {
        [updatedCharger] = await trx('chargers')
          .where({ id, user_id: userId })
          .update({
            ...safeChargerUpdate,
            updated_at: new Date().toISOString(),
          })
          .returning('*');
      } else {
        updatedCharger = await trx('chargers')
          .where({ id, user_id: userId })
          .first();
      }

      if (!updatedCharger) {
        await trx.rollback();
        return res
          .status(404)
          .json({ success: false, message: 'Carregador não encontrado' });
      }

      if (Object.keys(config).length > 0) {
        const existing = await trx('charger_configs')
          .where({ charger_id: id })
          .first();

        const data = {
          ...config,
          config_received_at: new Date().toISOString(),
        };

        if (existing) {
          await trx('charger_configs')
            .where({ charger_id: id })
            .update(data);
        } else {
          await trx('charger_configs')
            .insert({ charger_id: id, ...data });
        }
      }

      await trx.commit();
      res.json({ success: true, data: updatedCharger });
    } catch (err) {
      await trx.rollback();
      console.error('Erro ao atualizar carregador:', err);
      res.status(500).json({ success: false, message: 'Erro no servidor' });
    }
  }
);

// DELETE /api/chargers/:id
router.delete(
  '/:id',
  [param('id').isUUID()],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const userId = req.user.id;

    try {
      const charger = await db('chargers')
        .where({ id, user_id: userId })
        .first();

      if (!charger) {
        return res
          .status(404)
          .json({ success: false, message: 'Carregador não encontrado' });
      }

      try {
        await axios.post(
          'http://localhost:7000/client/delete',
          {
            client_name: charger.mqtt_user,
            chargeID: charger.mqtt_topic,
          },
          { timeout: 5000 }
        );
      } catch (err) {
        console.error(
          'Erro ao deletar cliente Mosquitto:',
          err?.response?.data || err.message
        );
      }

      await db('chargers').where({ id }).del();
      res.json({ success: true, message: 'Carregador excluído com sucesso' });
    } catch (err) {
      console.error('Erro ao excluir carregador:', err);
      res.status(500).json({ success: false, message: 'Erro no servidor' });
    }
  }
);

// PUT /api/chargers/:id/config
router.put(
  '/:id/config',
  [param('id').isUUID(), body('config').isObject()],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const { config } = req.body;
    const userId = req.user.id;

    try {
      const charger = await db('chargers')
        .where({ id, user_id: userId })
        .first();

      if (!charger) {
        return res.status(404).json({
          success: false,
          message: 'Charger not found or unauthorized',
        });
      }

      // lê config atual
      const existing = await db('charger_configs')
        .where({ charger_id: id })
        .first();

      // throttle simples por charger
      const nowMs = Date.now();
      const lastMs = lastConfigUpdateAt.get(id) || 0;
      const tooSoon = nowMs - lastMs < 800;

      // se veio max_charging_current, normaliza/clampa
      let safeConfig = { ...config };
      if (safeConfig.max_charging_current !== undefined) {
        const safeAmp = Math.max(6, Math.min(Number(safeConfig.max_charging_current), 64));
        safeConfig.max_charging_current = safeAmp;
      }

      const onlyAmp =
        Object.keys(safeConfig).length === 1 &&
        safeConfig.max_charging_current !== undefined;

      // dedupe: mesmo valor -> responde ok sem update nem mqtt
      if (
        existing &&
        onlyAmp &&
        existing.max_charging_current === safeConfig.max_charging_current
      ) {
        return res.json({ success: true, data: existing, message: 'Config unchanged' });
      }

      // se é update muito rápido e igual ao que já está, ignora
      if (tooSoon && existing && onlyAmp) {
        return res.json({ success: true, data: existing, message: 'Throttled' });
      }

      // ✅ upsert
      let updatedConfig;
      if (existing) {
        [updatedConfig] = await db('charger_configs')
          .where({ charger_id: id })
          .update({
            ...safeConfig,
            config_received_at: new Date().toISOString(),
          })
          .returning('*');
      } else {
        [updatedConfig] = await db('charger_configs')
          .insert({
            charger_id: id,
            ...safeConfig,
            config_received_at: new Date().toISOString(),
          })
          .returning('*');
      }

      lastConfigUpdateAt.set(id, nowMs);

      const keyMap = {
        max_charging_current: 'maxChargingCurrent',
        require_auth: 'requireAuth',
        temperature_limit: 'temperatureThreshold',
      };

      Object.entries(safeConfig).forEach(([dbKey, value]) => {
        const publishKey = keyMap[dbKey] || dbKey;
        mqttClient.sendConfig(charger.mqtt_topic, publishKey, value);
      });

      return res.json({ success: true, data: updatedConfig });
    } catch (err) {
      console.error('Error updating config:', err);
      return res
        .status(500)
        .json({ success: false, message: 'Server error' });
    }
  }
);

// GET /api/chargers/:id/schedule
router.get(
  '/:id/schedule',
  [param('id').isUUID()],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const userId = req.user.id;

    const charger = await db('chargers')
      .where({ id, user_id: userId })
      .first();

    if (!charger) {
      return res
        .status(404)
        .json({ success: false, message: 'Carregador não encontrado' });
    }

    try {
      const schedules = await db('charger_schedules')
        .where({ charger_id: id })
        .orderBy('created_at', 'desc');

      return res.json({ success: true, data: schedules });
    } catch (err) {
      console.error('Erro ao buscar schedules:', err);
      return res.json({ success: true, data: [] });
    }
  }
);

// POST /api/chargers/:id/schedule
router.post(
  '/:id/schedule',
  [
    param('id').isUUID(),
    body('start').matches(/^\d{2}:\d{2}$/).withMessage('start inválido'),
    body('end').matches(/^\d{2}:\d{2}$/).withMessage('end inválido'),
    body('repeat')
      .isIn(['everyday', 'weekdays', 'weekends'])
      .withMessage('repeat inválido'),
  ],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const { start, end, repeat } = req.body;
    const userId = req.user.id;

    const charger = await db('chargers')
      .where({ id, user_id: userId })
      .first();

    if (!charger) {
      return res
        .status(404)
        .json({ success: false, message: 'Carregador não encontrado' });
    }

    try {
      const [inserted] = await db('charger_schedules')
        .insert({
          charger_id: id,
          start,
          end,
          repeat,
          created_at: new Date().toISOString(),
        })
        .returning('*');

      return res.status(201).json({ success: true, data: inserted });
    } catch (err) {
      console.error('Erro ao criar schedule:', err);
      return res
        .status(500)
        .json({ success: false, message: 'Erro ao criar schedule' });
    }
  }
);

// POST /api/chargers/:id/action
router.post(
  '/:id/action',
  [
    param('id').isUUID().withMessage('ID do carregador inválido'),
    body('action').isIn(['start', 'stop']).withMessage('Ação inválida'),
    body('ampLimit')
      .optional()
      .isInt({ min: 6, max: 64 })
      .withMessage('ampLimit deve estar entre 6 e 64'),
  ],
  handleValidation,
  async (req, res) => {
    const { id } = req.params;
    const { action, ampLimit } = req.body;
    const userId = req.user.id;

    try {
      const charger = await db('chargers')
        .where({ id, user_id: userId })
        .first();

      if (!charger) {
        return res.status(404).json({
          success: false,
          message: 'Carregador não encontrado ou não autorizado',
        });
      }

      if (ampLimit !== undefined) {
        const safeAmp = Math.max(6, Math.min(Number(ampLimit), 64));

        await db('charger_configs')
          .where({ charger_id: id })
          .update({
            max_charging_current: safeAmp,
            config_received_at: new Date().toISOString(),
          });

        mqttClient.sendConfig(
          charger.mqtt_topic,
          'maxChargingCurrent',
          safeAmp
        );
      }

      const enable = action === 'start';
      mqttClient.sendEnable(charger.mqtt_topic, enable);

      return res.json({
        success: true,
        message: `Comando '${action}' enviado com sucesso`,
      });
    } catch (err) {
      console.error(
        `Erro ao processar ação '${action}' para carregador ${id}:`,
        err
      );
      return res
        .status(500)
        .json({ success: false, message: 'Erro ao processar ação' });
    }
  }
);

module.exports = router;
