const db = require('../db');
const { haversine, AppError } = require('../utils/helpers');

async function saveLocation({ device_public_id, ingest_secret, lat, lng, speed, direction, accuracy }) {
  const [devices] = await db.query(
    'SELECT * FROM devices WHERE public_id = ? AND ingest_secret = ?',
    [device_public_id, ingest_secret],
  );
  const device = devices[0];
  if (!device) throw new AppError(403, 'Device credentials rejected');

  const point = {
    lat: Number(lat),
    lng: Number(lng),
    speed: speed == null ? null : Number(speed),
    direction: direction == null ? null : Number(direction),
    accuracy: accuracy == null ? 8 : Number(accuracy),
  };

  await db.query(
    `INSERT INTO locations (device_id, recorded_at, lat, lng, speed, direction, accuracy)
     VALUES (?, NOW(), ?,?,?,?,?)`,
    [device.id, point.lat, point.lng, point.speed, point.direction, point.accuracy],
  );

  const wasOffline = device.status === 'offline';
  await db.query('UPDATE devices SET last_seen = NOW(), status = "online" WHERE id = ?', [device.id]);

  if (wasOffline) {
    await addAlert(device.id, 'online', 'Tracker is online', point.lat, point.lng);
  }
  if (point.speed != null && point.speed > 80) {
    await addAlert(device.id, 'overspeed', `Speed ${point.speed} km/h`, point.lat, point.lng);
  }

  await checkPositionLock(device.id, point);
  await checkGeofences(device.id, point);

  return { ok: true };
}

async function addAlert(deviceId, type, message, lat, lng) {
  await db.query(
    'INSERT INTO alerts (device_id, type, message, lat, lng) VALUES (?,?,?,?,?)',
    [deviceId, type, message, lat, lng],
  );
}

async function checkPositionLock(deviceId, point) {
  const [locks] = await db.query(
    'SELECT * FROM position_locks WHERE device_id = ? AND status = "ON"',
    [deviceId],
  );
  const lock = locks[0];
  if (!lock || point.accuracy > 30) return;

  const dist = haversine(Number(lock.lat), Number(lock.lng), point.lat, point.lng);
  if (dist > Number(lock.radius_m)) {
    const consec = Number(lock.consecutive_outside) + 1;
    await db.query('UPDATE position_locks SET consecutive_outside = ? WHERE id = ?', [
      consec,
      lock.id,
    ]);
    if (consec >= 2) {
      await addAlert(
        deviceId,
        'position_lock',
        `Vehicle moved ${Math.round(dist)} m from locked position`,
        point.lat,
        point.lng,
      );
      await db.query('UPDATE position_locks SET consecutive_outside = 0 WHERE id = ?', [lock.id]);
    }
  } else {
    await db.query('UPDATE position_locks SET consecutive_outside = 0 WHERE id = ?', [lock.id]);
  }
}

async function checkGeofences(deviceId, point) {
  const [fences] = await db.query(
    'SELECT * FROM geofences WHERE device_id = ? AND active = 1',
    [deviceId],
  );

  for (const fence of fences) {
    const inside =
      haversine(Number(fence.lat), Number(fence.lng), point.lat, point.lng) <=
      Number(fence.radius_m);

    if (fence.last_inside !== null && Number(fence.last_inside) !== Number(inside)) {
      await addAlert(
        deviceId,
        inside ? 'geofence_enter' : 'geofence_exit',
        `${inside ? 'Entered' : 'Left'} ${fence.name}`,
        point.lat,
        point.lng,
      );
    }
    await db.query('UPDATE geofences SET last_inside = ? WHERE id = ?', [
      inside ? 1 : 0,
      fence.id,
    ]);
  }
}

module.exports = { saveLocation };
