/**
 * controller.ts (tracking)
 *
 * Dos endpoints:
 *   POST /gps-update → interno (simulador / admin). No expone datos del user.
 *   GET  /status     → protegido por AuthMiddleware. Devuelve SOLO la
 *                      información del usuario logueado (visión de túnel).
 */

import { Request, Response } from "express";
import { AuthRequest } from "../middlewares/auth.middleware.js";
import { CustomError } from "../../domain/errors/custom.error.js";
import { CalculateEtaUseCase } from "../../domain/use-cases/notifications/calculate-eta.use-case.js";
import { EvaluateNotificationUseCase } from "../../domain/use-cases/notifications/evaluate-notification.use-case.js";
import { ProcessGpsUpdateUseCase } from "../../domain/use-cases/notifications/process-gps-update.use-case.js";
import { GetUserStatusUseCase } from "../../domain/use-cases/tracking/get-user-status.use-case.js";
import { InMemoryNotificationCache } from "../../data/cache/notification-cache.impl.js";
import { InMemoryRouteStateRepository } from "../../data/cache/route-state.impl.js";
import { InMemoryNotificationInbox } from "../../data/cache/notification-inbox.impl.js";
import { RouteSimulator } from "../../data/simulation/route-simulator.js";

export class TrackingController {
  // Singletons del módulo (viven mientras corre el server)
  private static dedupCache = new InMemoryNotificationCache();
  private static routeState = new InMemoryRouteStateRepository();
  private static inbox = new InMemoryNotificationInbox();

  private calculateEta = new CalculateEtaUseCase();
  private evaluateNotification = new EvaluateNotificationUseCase(
    TrackingController.dedupCache,
  );
  private processGpsUpdate = new ProcessGpsUpdateUseCase(
    this.calculateEta,
    this.evaluateNotification,
    TrackingController.routeState,
    TrackingController.inbox,
  );
  private getUserStatus = new GetUserStatusUseCase(
    TrackingController.routeState,
    TrackingController.inbox,
  );

  /** Permite que app.ts lance el simulador usando estos mismos singletons. */
  buildSimulator(): RouteSimulator {
    return new RouteSimulator(this.processGpsUpdate);
  }

  gpsUpdate = async (req: Request, res: Response) => {
    try {
      const result = await this.processGpsUpdate.execute(req.body);
      res.status(200).json(result);
    } catch (error) {
      this.handleError(error, res);
    }
  };

  myStatus = async (req: AuthRequest, res: Response) => {
    try {
      if (!req.user) throw CustomError.unauthorized("User not authenticated");
      const result = await this.getUserStatus.execute(req.user.id);
      res.status(200).json(result);
    } catch (error) {
      this.handleError(error, res);
    }
  };

  /**
   * POST /reset-demo
   * Borra cache de dedup, inbox y estado de rutas.
   * Permite repetir la demo sin reiniciar el server. Solo para hackathon.
   */
  resetDemo = async (_req: Request, res: Response) => {
    try {
      await TrackingController.dedupCache.clear();
      await TrackingController.inbox.clearAll();
      await TrackingController.routeState.clearAll();

      res
        .status(200)
        .json({ message: "Demo state reset — simulator continues running" });
    } catch (error) {
      this.handleError(error, res);
    }
  };

  private handleError(error: unknown, res: Response): void {
    if (error instanceof CustomError) {
      res.status(error.statusCode).json({ error: error.message });
      return;
    }
    if (error instanceof Error) {
      res.status(400).json({ error: error.message });
      return;
    }
    res.status(500).json({ error: "Internal server error" });
  }
}
