import { Router } from "express";

import { authenticateToken } from "../middleware/auth.js";
import {
  checkDeviceUsernameAvailability,
  clearTrackingLocations,
  createDevice,
  createGeofence,
  deactivateDevice,
  deleteGeofence,
  executeFreeKioskAction,
  assignGeofenceDevices,
  acknowledgeDeviceCommand,
  getDeviceById,
  getDeviceCsvExport,
  getDeviceKmlExport,
  getDeviceLive,
  getDeviceLocations,
  getDeviceQrData,
  getTrackingDevicesQuickCount,
  getScreenShareSession,
  getDeviceStats,
  getDeviceTrips,
  getAlertSummary,
  getServerBaseUrl,
  getTrackingSettings,
  getStats,
  listAlerts,
  listTrackingAudit,
  listDevices,
  listGeofences,
  mobileLogin,
  pollDeviceCommands,
  queueDeviceCommand,
  requestScreenShareSession,
  readAllAlerts,
  receiveHeartbeat,
  receiveLocation,
  resetDevicePassword,
  resolveCompanyIdFromRequest,
  startScreenShareSession,
  stopScreenShareSession,
  uploadDeviceScreenshot,
  uploadScreenShareFrame,
  updateTrackingSettings,
  updateAlertStatus,
  updateDevice,
  updateGeofence,
  verifyMobileToken,
} from "../services/trackingService.js";
import { ApiError } from "../utils/ApiError.js";

const router = Router();

const logMobileRequest = (label, req, extra = {}) => {
  console.log(`[tracking-mobile] ${label}`, {
    ip: req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip || null,
    userAgent: req.headers["user-agent"] || null,
    ...extra,
  });
};

const asyncHandler = (handler) => async (req, res) => {
  try {
    await handler(req, res);
  } catch (error) {
    const statusCode = error instanceof ApiError ? error.statusCode : 500;
    const message = error instanceof ApiError ? error.message : "Erreur serveur.";

    if (!(error instanceof ApiError)) {
      console.error("Tracking route error", error);
    }

    res.status(statusCode).json({ message });
  }
};

router.post(
  "/mobile/login",
  asyncHandler(async (req, res) => {
    logMobileRequest("login", req, {
      username: req.body.username || null,
      deviceInfo: req.body.device_info || req.body.deviceInfo || {},
    });

    const result = await mobileLogin({
      username: req.body.username,
      password: req.body.password,
      deviceInfo: req.body.device_info || req.body.deviceInfo || {},
    });

    res.json(result);
  }),
);

router.post(
  "/mobile/location",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("location", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      body: req.body,
    });

    const result = await receiveLocation({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      locationData: req.body,
    });

    res.json(result);
  }),
);

router.post(
  "/mobile/commands/poll",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("commands:poll", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
    });

    const items = await pollDeviceCommands({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
    });

    res.json({ items });
  }),
);

router.post(
  "/mobile/commands/:id/ack",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("commands:ack", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      commandId: Number(req.params.id),
      body: req.body,
    });

    const item = await acknowledgeDeviceCommand({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      commandId: Number(req.params.id),
      status: req.body.status,
      result: req.body.result || null,
    });

    res.json({ item });
  }),
);

router.post(
  "/mobile/screen-share/session/start",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("screen-share:start", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      body: req.body,
    });

    const item = await startScreenShareSession({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      sessionId: Number(req.body.session_id),
      width: req.body.width ? Number(req.body.width) : null,
      height: req.body.height ? Number(req.body.height) : null,
    });

    res.json({ item });
  }),
);

router.post(
  "/mobile/screen-share/session/:id/frame",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("screen-share:frame", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      sessionId: Number(req.params.id),
    });

    const item = await uploadScreenShareFrame({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      sessionId: Number(req.params.id),
      imageBase64: req.body.image_base64,
      capturedAt: req.body.captured_at,
      width: req.body.width ? Number(req.body.width) : null,
      height: req.body.height ? Number(req.body.height) : null,
    });

    res.json({ item });
  }),
);

router.post(
  "/mobile/screen-share/session/:id/stop",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("screen-share:stop", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      sessionId: Number(req.params.id),
      body: req.body,
    });

    const item = await stopScreenShareSession({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      sessionId: Number(req.params.id),
      reason: req.body.reason || null,
    });

    res.json({ item });
  }),
);

router.post(
  "/mobile/screenshot",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("screenshot", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
    });

    const item = await uploadDeviceScreenshot({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      capturedAt: req.body.captured_at,
      imageBase64: req.body.image_base64,
    });

    res.json({ item });
  }),
);

router.post(
  "/mobile/heartbeat",
  verifyMobileToken,
  asyncHandler(async (req, res) => {
    logMobileRequest("heartbeat", req, {
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      body: req.body,
    });

    const result = await receiveHeartbeat({
      deviceId: req.mobileDevice.deviceId,
      companyId: req.mobileDevice.companyId,
      payload: req.body,
    });

    res.json(result);
  }),
);

router.use(authenticateToken);

router.get(
  "/devices",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const items = await listDevices(companyId, {
      includeInactive: req.query.include_inactive !== "false",
    });
    res.json({ items });
  }),
);

router.get(
  "/devices/live-states",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const items = await listDevices(companyId);
    res.json({ items });
  }),
);

router.get(
  "/devices/quick-count",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getTrackingDevicesQuickCount(companyId);
    res.json({ item });
  }),
);

router.get(
  "/devices/check-username",
  asyncHandler(async (req, res) => {
    const item = await checkDeviceUsernameAvailability({
      username: req.query.username,
      excludeDeviceId: req.query.exclude_id ? Number(req.query.exclude_id) : null,
    });

    res.json({ item });
  }),
);

router.post(
  "/devices/:id/commands",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await queueDeviceCommand({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      commandType: req.body.command_type,
      payload: req.body.payload || {},
      ipAddress: req.ip,
    });

    res.status(201).json({ item });
  }),
);

router.post(
  "/devices/:id/screen-share/request",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await requestScreenShareSession({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      ipAddress: req.ip,
    });

    res.status(201).json({ item });
  }),
);

router.post(
  "/devices/:id/freekiosk/:action",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await executeFreeKioskAction({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      action: req.params.action,
      payload: req.body || {},
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.get(
  "/screen-share/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getScreenShareSession({
      companyId,
      sessionId: Number(req.params.id),
    });

    res.json({ item });
  }),
);

router.post(
  "/devices",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const result = await createDevice({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      payload: req.body,
      ipAddress: req.ip,
      serverUrl: getServerBaseUrl(req),
    });

    res.status(201).json(result);
  }),
);

router.put(
  "/devices/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await updateDevice({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      payload: req.body,
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.get(
  "/devices/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getDeviceById({
      companyId,
      deviceId: Number(req.params.id),
    });

    res.json({ item });
  }),
);

router.delete(
  "/devices/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await deactivateDevice({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.get(
  "/devices/:id/locations",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    let from = req.query.from;
    let to = req.query.to;

    if (req.query.date) {
      from = `${req.query.date}T00:00:00.000Z`;
      to = `${req.query.date}T23:59:59.999Z`;
    }

    const items = await getDeviceLocations({
      companyId,
      deviceId: Number(req.params.id),
      from,
      to,
      limit: req.query.limit,
    });

    res.json({ items });
  }),
);

router.get(
  "/devices/:id/trips",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const items = await getDeviceTrips({
      companyId,
      deviceId: Number(req.params.id),
      from: req.query.from,
      to: req.query.to,
    });

    res.json({ items });
  }),
);

router.get(
  "/devices/:id/stats",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getDeviceStats({
      companyId,
      deviceId: Number(req.params.id),
      from: req.query.from,
      to: req.query.to,
    });

    res.json({ item });
  }),
);

router.get(
  "/devices/:id/live",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getDeviceLive({
      companyId,
      deviceId: Number(req.params.id),
    });

    res.json({ item });
  }),
);

router.post(
  "/devices/:id/reset-password",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await resetDevicePassword({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      newPassword: req.body.password,
      ipAddress: req.ip,
      serverUrl: getServerBaseUrl(req),
    });

    res.json(item);
  }),
);

router.get(
  "/devices/:id/qr-data",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getDeviceQrData({
      companyId,
      deviceId: Number(req.params.id),
      serverUrl: getServerBaseUrl(req),
    });

    res.json(item);
  }),
);

router.get(
  "/devices/:id/export/kml",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getDeviceKmlExport({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      date: req.query.date,
      ipAddress: req.ip,
    });

    res.setHeader("Content-Type", "application/vnd.google-earth.kml+xml; charset=utf-8");
    res.setHeader("Content-Disposition", `attachment; filename="${item.filename}"`);
    res.send(item.content);
  }),
);

router.get(
  "/devices/:id/export/csv",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getDeviceCsvExport({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      deviceId: Number(req.params.id),
      date: req.query.date,
      ipAddress: req.ip,
    });

    res.setHeader("Content-Type", "text/csv; charset=utf-8");
    res.setHeader("Content-Disposition", `attachment; filename="${item.filename}"`);
    res.send(item.content);
  }),
);

router.get(
  "/geofences",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const items = await listGeofences(companyId);
    res.json({ items });
  }),
);

router.post(
  "/geofences",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await createGeofence({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      payload: req.body,
      ipAddress: req.ip,
    });

    res.status(201).json({ item });
  }),
);

router.put(
  "/geofences/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await updateGeofence({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      geofenceId: Number(req.params.id),
      payload: req.body,
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.post(
  "/geofences/:id/devices",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await assignGeofenceDevices({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      geofenceId: Number(req.params.id),
      deviceIds: req.body.device_ids,
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.delete(
  "/geofences/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    await deleteGeofence({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      geofenceId: Number(req.params.id),
      ipAddress: req.ip,
    });

    res.status(204).send();
  }),
);

router.get(
  "/alerts",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const items = await listAlerts({
      companyId,
      status: req.query.status || null,
      type: req.query.type || null,
      deviceId: req.query.device_id ? Number(req.query.device_id) : null,
      from: req.query.from || null,
      to: req.query.to || null,
    });

    res.json({ items });
  }),
);

router.get(
  "/alerts/summary",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getAlertSummary(companyId);
    res.json({ item });
  }),
);

router.put(
  "/alerts/read-all",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await readAllAlerts({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      ipAddress: req.ip,
    });
    res.json({ item });
  }),
);

router.put(
  "/alerts/:id",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await updateAlertStatus({
      companyId,
      alertId: Number(req.params.id),
      status: req.body.status,
      actorId: Number(req.user.id || req.user.sub),
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.put(
  "/alerts/:id/read",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await updateAlertStatus({
      companyId,
      alertId: Number(req.params.id),
      status: "read",
      actorId: Number(req.user.id || req.user.sub),
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.get(
  "/settings",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getTrackingSettings(companyId);
    res.json({ item });
  }),
);

router.get(
  "/audit",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const result = await listTrackingAudit({
      companyId,
      userId: req.query.user_id ? Number(req.query.user_id) : null,
      action: req.query.action || null,
      from: req.query.from || null,
      to: req.query.to || null,
    });

    res.json(result);
  }),
);

router.put(
  "/settings",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await updateTrackingSettings({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      payload: req.body,
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.post(
  "/locations/clear",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await clearTrackingLocations({
      companyId,
      actorId: Number(req.user.id || req.user.sub),
      month: req.body.month,
      deviceId: req.body.device_id ? Number(req.body.device_id) : null,
      ipAddress: req.ip,
    });

    res.json({ item });
  }),
);

router.get(
  "/stats",
  asyncHandler(async (req, res) => {
    const companyId = resolveCompanyIdFromRequest(req);
    const item = await getStats({
      companyId,
      deviceId: req.query.device_id ? Number(req.query.device_id) : null,
      from: req.query.from,
      to: req.query.to,
    });

    res.json({ item });
  }),
);

export default router;
