const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '..', '.env') });
const express = require('express');
const mysql = require('mysql2/promise');

const app = express();
const PORT = process.env.PORT || 3002;

app.use(express.json({ limit: '5mb' }));
app.use((req, res, next) => {
  req.headers.accept = 'application/json';
  next();
});

// Pool principal (revisa) - acepta DB_USER o DB_USERNAME (Laravel)
const dbUser = process.env.DB_USER || process.env.DB_USERNAME;
const dbPassword = process.env.DB_PASSWORD;
const poolConfig = {
  host: process.env.DB_HOST || '127.0.0.1',
  port: parseInt(process.env.DB_PORT, 10) || 3306,
  user: dbUser,
  password: dbPassword,
  database: process.env.DB_DATABASE || 'revisa',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0,
  connectTimeout: 10000
};
let pool = null;
if (poolConfig.user && poolConfig.password) {
  pool = mysql.createPool(poolConfig);
}

// Pool GPS (gpsimple) - opcional
let gpsPool = null;
if (process.env.GPS_DB_HOST && process.env.GPS_DB_USER && process.env.GPS_DB_PASSWORD) {
  gpsPool = mysql.createPool({
    host: process.env.GPS_DB_HOST,
    port: parseInt(process.env.GPS_DB_PORT, 10) || 3306,
    user: process.env.GPS_DB_USER,
    password: process.env.GPS_DB_PASSWORD,
    database: process.env.GPS_DB_DATABASE || 'gpsimple',
    waitForConnections: true,
    connectionLimit: 5,
    connectTimeout: 10000
  });
}

function cleanRut(rut) {
  return String(rut).replace(/[\s.]/g, '').trim();
}

function validateRut(rut) {
  const s = cleanRut(rut).toUpperCase();
  const m = s.match(/^(\d{7,8})-?([\dK])$/);
  if (!m) return false;
  let sum = 0, mul = 2;
  for (let i = m[1].length - 1; i >= 0; i--) {
    sum += parseInt(m[1][i], 10) * mul;
    mul = mul === 7 ? 2 : mul + 1;
  }
  let dv = 11 - (sum % 11);
  dv = dv === 11 ? '0' : dv === 10 ? 'K' : String(dv);
  return m[2] === dv;
}

const MOTIVO_MAP = {
  instalacion_gps: 'Instalación GPS',
  desinstalacion_gps: 'Desinstalación GPS',
  visita_tecnica: 'Visita Técnica',
  visita_fallida: 'Visita Fallida',
  servicio_mecanico: 'Servicio Mecánico'
};
const TALLER_LOGOS = {
  cartec: 'https://media.recuperauto.cl/Media/Cartec/cartec-logo.png',
  domicilio: 'https://media.recuperauto.cl/Media/Cartec/cartec-logo.png',
  taller_externo: 'https://media.recuperauto.cl/Media/Cartec/taller-externo-logo.png'
};
const GPS_LOGOS = {
  gpsimple: 'https://media.recuperauto.cl/Media/Cartec/gpsimple-logo.png',
  entel: 'https://media.recuperauto.cl/Media/Cartec/entel-logo.png',
  tattersall: 'https://media.recuperauto.cl/Media/Cartec/tattersall-logo.png',
  cemin: 'https://media.recuperauto.cl/Media/Cartec/cemin-logo.png',
  carflex: 'https://media.recuperauto.cl/Media/Cartec/carflex-logo.png'
};

const PRES_GPS = ['gpsimple', 'entel', 'tattersall', 'cemin', 'carflex'];

function getPool() {
  if (!pool) throw new Error('DB no configurada');
  return pool;
}

// ----- Health -----
app.get('/api/health', (req, res) => {
  let dbStatus = 'not tested';
  if (req.query.db === '1' && pool) {
    pool.getConnection()
      .then(conn => { conn.release(); return 'connected'; })
      .catch(err => 'error: ' + err.message)
      .then(s => res.json({ success: true, message: 'API CartecWeb operativa', version: '1.0.0', timestamp: new Date().toISOString(), db: s }))
      .catch(() => res.json({ success: true, message: 'API CartecWeb operativa', version: '1.0.0', timestamp: new Date().toISOString(), db: dbStatus }));
  } else {
    res.json({ success: true, message: 'API CartecWeb operativa', version: '1.0.0', timestamp: new Date().toISOString(), db: dbStatus });
  }
});

// ----- OT list -----
app.get('/api/ot/list', async (req, res) => {
  try {
    const p = getPool();
    const estado = req.query.estado;
    const rut = req.query.rut_tecnico;
    const limit = Math.min(parseInt(req.query.limit, 10) || 10, 100) || 10;
    let sql = 'SELECT id_ot, rut_cliente, rut_tecnico, empresa, estado_ot, motivo, creado_en FROM OT';
    const params = [];
    const where = [];
    if (rut) { where.push('rut_tecnico = ?'); params.push(rut); }
    if (estado) { where.push('estado_ot = ?'); params.push(estado); }
    if (where.length) sql += ' WHERE ' + where.join(' AND ');
    sql += ' ORDER BY id_ot DESC LIMIT ' + parseInt(limit, 10);
    const [rows] = await p.execute(sql, params);
    res.json({ success: true, count: rows.length, data: rows });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- OT latest -----
app.get('/api/ot/latest', async (req, res) => {
  try {
    const [rows] = await getPool().execute(
      'SELECT id_ot, rut_cliente, rut_tecnico, empresa, estado_ot, motivo, creado_en FROM OT ORDER BY id_ot DESC LIMIT 10'
    );
    res.json({ success: true, count: rows.length, data: rows });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- OT by id -----
app.get('/api/ot/:id', async (req, res) => {
  try {
    const p = getPool();
    const id = req.params.id;
    const [otRows] = await p.execute('SELECT * FROM OT WHERE id_ot = ?', [id]);
    if (!otRows.length) return res.status(404).json({ success: false, error: 'Orden de trabajo no encontrada' });
    const [vehiculos] = await p.execute('SELECT * FROM OT_Vehiculos WHERE id_ot = ?', [id]);
    const [clientes] = await p.execute('SELECT * FROM Clientes WHERE rut_cliente = ?', [otRows[0].rut_cliente]);
    res.json({
      success: true,
      data: { ot: otRows[0], cliente: clientes[0] || null, vehiculos }
    });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- OT create -----
app.post('/api/ot/create', async (req, res) => {
  const b = req.body;
  const cliente = b && b.cliente;
  const tecnico = b && b.tecnico;
  const servicio = b && b.servicio;
  const vehiculos = b && b.vehiculos;

  if (!cliente || !cliente.rut || !cliente.nombre || !cliente.direccion || !cliente.comuna) {
    return res.status(422).json({ success: false, error: 'Faltan campos en cliente (rut, nombre, direccion, comuna)' });
  }
  if (!tecnico || !tecnico.rut) {
    return res.status(422).json({ success: false, error: 'tecnico.rut es requerido' });
  }
  if (!servicio || !PRES_GPS.includes(servicio.presGPS) || !servicio.tiposerv || !servicio.taller_install) {
    return res.status(422).json({ success: false, error: 'servicio requiere presGPS (gpsimple|entel|...), tiposerv, taller_install' });
  }
  if (!Array.isArray(vehiculos) || !vehiculos.length || !vehiculos.every(v => v && v.patente)) {
    return res.status(422).json({ success: false, error: 'vehiculos debe ser un array con al menos un objeto con patente' });
  }

  const rutCliente = cleanRut(cliente.rut);
  if (!validateRut(rutCliente)) {
    return res.status(400).json({ success: false, error: 'RUT de cliente inválido' });
  }

  const conn = await getPool().getConnection();
  try {
    await conn.beginTransaction();

    await conn.execute(
      `INSERT INTO Clientes (rut_cliente, nombre, direccion, comuna, telefono, email)
       VALUES (?, ?, ?, ?, ?, ?)
       ON DUPLICATE KEY UPDATE nombre = VALUES(nombre), direccion = VALUES(direccion), comuna = VALUES(comuna), telefono = VALUES(telefono), email = VALUES(email)`,
      [rutCliente, cliente.nombre, cliente.direccion, cliente.comuna, cliente.telefono || null, cliente.email || null]
    );

    const rutTecnico = cleanRut(tecnico.rut);
    const motivo = MOTIVO_MAP[servicio.tiposerv] || servicio.tiposerv;
    const logoTaller = TALLER_LOGOS[servicio.taller_install] || TALLER_LOGOS.cartec;
    const logoServicio = GPS_LOGOS[servicio.presGPS] || null;
    const now = new Date();
    const fechaServ = now.toISOString().slice(0, 10);

    const [ins] = await conn.execute(
      `INSERT INTO OT (rut_cliente, rut_tecnico, empresa, taller, direccion_instalacion, comuna_instalacion, telefono_instalacion, email_instalacion, estado_ot, motivo, logo_taller, logo_servicio, fecha, fecha_serv, creado_en, actualizado_en)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pendiente', ?, ?, ?, ?, ?, ?, ?)`,
      [rutCliente, rutTecnico, servicio.presGPS, servicio.taller_install, cliente.direccion, cliente.comuna, cliente.telefono || null, cliente.email || null, motivo, logoTaller, logoServicio, now, fechaServ, now, now]
    );
    const idOt = ins.insertId;

    for (const v of vehiculos) {
      const patente = String(v.patente).trim().toUpperCase();
      await conn.execute(
        `INSERT INTO Vehiculos (patente, marca, modelo, anio, color) VALUES (?, ?, ?, ?, ?)
         ON DUPLICATE KEY UPDATE marca = VALUES(marca), modelo = VALUES(modelo), anio = VALUES(anio), color = VALUES(color)`,
        [patente, v.marca || null, v.modelo || null, v.anio || null, v.color || 'Sin especificar']
      );
      await conn.execute('INSERT IGNORE INTO OT_Vehiculos (id_ot, patente) VALUES (?, ?)', [idOt, patente]);
    }

    await conn.commit();
    res.status(201).json({ success: true, message: 'Orden de trabajo creada exitosamente', id_ot: idOt });
  } catch (e) {
    await conn.rollback();
    res.status(500).json({ success: false, error: e.message });
  } finally {
    conn.release();
  }
});

// ----- OT complete -----
app.patch('/api/ot/:id/complete', async (req, res) => {
  try {
    const id = req.params.id;
    const b = req.body || {};
    const [rows] = await getPool().execute('SELECT id_ot FROM OT WHERE id_ot = ?', [id]);
    if (!rows.length) return res.status(404).json({ success: false, error: 'Orden de trabajo no encontrada' });

    const updates = ['estado_ot = ?', 'actualizado_en = ?'];
    const params = ['completado', new Date()];
    if (b.hora_llegada) { updates.push('hora_llegada = ?'); params.push(b.hora_llegada); }
    if (b.hora_salida) { updates.push('hora_salida = ?'); params.push(b.hora_salida); }
    if (b.observaciones) { updates.push('obser_ot = ?'); params.push(b.observaciones); }
    if (b.firma && b.firma.rut) {
      updates.push('rut_contacto = ?');
      params.push(cleanRut(b.firma.rut));
      if (b.firma.imagen) {
        let img = b.firma.imagen;
        if (img.includes('base64,')) img = img.split('base64,')[1];
        updates.push('imgfirma = ?');
        params.push(Buffer.from(img, 'base64'));
      }
    }
    params.push(id);
    await getPool().execute(`UPDATE OT SET ${updates.join(', ')} WHERE id_ot = ?`, params);
    res.json({ success: true, message: 'Orden de trabajo actualizada exitosamente' });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- Cliente -----
app.get('/api/cliente/:rut', async (req, res) => {
  const rutClean = cleanRut(req.params.rut);
  if (!validateRut(rutClean)) return res.status(400).json({ success: false, error: 'RUT inválido' });
  try {
    const [rows] = await getPool().execute('SELECT * FROM Clientes WHERE rut_cliente = ?', [rutClean]);
    if (!rows.length) return res.status(404).json({ success: false, error: 'Cliente no encontrado' });
    res.json({ success: true, data: rows[0] });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- GPS (requiere GPS pool) -----
app.get('/api/gps/position/:imei', async (req, res) => {
  if (!gpsPool) return res.status(503).json({ success: false, error: 'GPS DB no configurada' });
  const imei = req.params.imei;
  if (!imei || imei.length < 10) return res.status(400).json({ success: false, error: 'IMEI inválido' });
  try {
    const [rows] = await gpsPool.execute(
      `SELECT gso.imei, gso.lat, gso.lng, gso.protocol, gso.device, gso.dt_tracker, gso.params
       FROM gpsimple.gs_objects AS gso
       JOIN gpsimple.gs_object_custom_fields AS gcf ON gcf.object_id = gso.id
       WHERE gcf.value = ? LIMIT 1`,
      [imei]
    );
    if (!rows.length) return res.status(404).json({ success: false, error: 'Dispositivo no encontrado' });
    res.json({ success: true, data: rows[0] });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

app.get('/api/gps/info/:id', async (req, res) => {
  if (!gpsPool) return res.status(503).json({ success: false, error: 'GPS DB no configurada' });
  try {
    const [rows] = await gpsPool.execute(
      `SELECT gso.imei, gso.lat, gso.lng, gso.protocol, gso.device, gso.dt_tracker, gso.params
       FROM gpsimple.gs_objects AS gso
       JOIN gpsimple.gs_object_custom_fields AS gcf ON gcf.object_id = gso.id
       WHERE gcf.value = ? LIMIT 1`,
      [req.params.id]
    );
    if (!rows.length) return res.status(404).json({ success: false, error: 'Vehículo no encontrado' });
    res.json({ success: true, data: rows[0] });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- Técnicos (GPS DB - instaladores) -----
app.get('/api/tecnicos', async (req, res) => {
  if (!gpsPool) return res.status(503).json({ success: false, error: 'GPS DB no configurada' });
  try {
    const [rows] = await gpsPool.execute('SELECT id, nombre, email, rut, empresa, rol FROM instaladores ORDER BY nombre');
    res.json({ success: true, count: rows.length, data: rows });
  } catch (e) {
    res.status(500).json({ success: false, error: e.message });
  }
});

// ----- Vehiculo VIN (externo - opcional) -----
app.get('/api/vehiculo/vin/:vin', (req, res) => {
  const vin = req.params.vin;
  if (vin.length < 11 || vin.length > 17) return res.status(400).json({ success: false, error: 'VIN inválido' });
  const https = require('https');
  const url = `https://api.carcloud.cl/v1/vehicles/${vin}`;
  https.get(url, (ext) => {
    let data = '';
    ext.on('data', c => data += c);
    ext.on('end', () => {
      try {
        const j = JSON.parse(data);
        res.json({ success: true, data: j });
      } catch (_) {
        res.status(500).json({ success: false, error: 'Error al consultar datos del vehículo' });
      }
    });
  }).on('error', () => res.status(500).json({ success: false, error: 'Error al consultar datos del vehículo' }));
});

// 404
app.use((req, res) => res.status(404).json({ success: false, error: 'No encontrado' }));

app.listen(PORT, () => {
  console.log('Cartec Node API en http://localhost:' + PORT);
  if (!pool) console.warn('DB no configurada: defina DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE en .env');
  if (!gpsPool) console.warn('GPS DB no configurada: endpoints /api/gps/* y /api/tecnicos no disponibles');
});
