/**
 * EventoGeocerca Controller
 * ERP Transportistas
 *
 * REST API endpoints for geofence events (entry/exit/permanence).
 * Adapted from erp-mecanicas-diesel MMD-014 GPS Integration
 * Module: MAI-006 Tracking
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  EventoGeocercaService,
  EventoGeocercaFilters,
  DateRange,
} from '../services/evento-geocerca.service';
import { TipoEventoGeocerca } from '../entities/evento-geocerca.entity';

interface TenantRequest extends Request {
  tenantId?: string;
  userId?: string;
}

export function createEventoGeocercaController(dataSource: DataSource): Router {
  const router = Router();
  const service = new EventoGeocercaService(dataSource);

  const extractTenant = (req: TenantRequest, res: Response, next: NextFunction) => {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'Tenant ID es requerido' });
    }
    req.tenantId = tenantId;
    req.userId = req.headers['x-user-id'] as string;
    next();
  };

  router.use(extractTenant);

  /**
   * Helper to parse date range from query parameters
   */
  const parseDateRange = (query: any): DateRange => {
    const now = new Date();
    const defaultStart = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); // 7 days ago
    return {
      fechaInicio: query.fechaDesde ? new Date(query.fechaDesde as string) : defaultStart,
      fechaFin: query.fechaHasta ? new Date(query.fechaHasta as string) : now,
    };
  };

  /**
   * List geofence events with filters
   * GET /api/gps/eventos-geocerca
   * Query params: geocercaId, unidadId, viajeId, tipoEvento, fechaDesde, fechaHasta, page, limit
   */
  router.get('/', async (req: TenantRequest, res: Response) => {
    try {
      const filters: EventoGeocercaFilters = {
        geocercaId: req.query.geocercaId as string,
        unidadId: req.query.unidadId as string,
        viajeId: req.query.viajeId as string,
        tipoEvento: req.query.tipoEvento as TipoEventoGeocerca,
        dispositivoId: req.query.dispositivoId as string,
      };

      const pagination = {
        page: parseInt(req.query.page as string, 10) || 1,
        limit: Math.min(parseInt(req.query.limit as string, 10) || 50, 200),
      };

      const result = await service.findAll(req.tenantId!, filters, pagination);
      res.json(result);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get geofence alerts (events marked with alerts)
   * GET /api/gps/eventos-geocerca/alertas
   */
  router.get('/alertas', async (req: TenantRequest, res: Response) => {
    try {
      const dateRange = parseDateRange(req.query);
      const limit = parseInt(req.query.limit as string, 10) || 50;

      // Get recent events and filter those with alerts
      const result = await service.findAll(
        req.tenantId!,
        {},
        { page: 1, limit: 500 }
      );

      const alertas = result.data.filter(
        (evento) =>
          evento.metadata?.alertaTriggered === true &&
          evento.tiempoEvento >= dateRange.fechaInicio &&
          evento.tiempoEvento <= dateRange.fechaFin
      );

      res.json({
        data: alertas.slice(0, limit),
        total: alertas.length,
        fechaDesde: dateRange.fechaInicio,
        fechaHasta: dateRange.fechaFin,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get summary statistics by geofence
   * GET /api/gps/eventos-geocerca/resumen/geocerca/:geocercaId
   */
  router.get('/resumen/geocerca/:geocercaId', async (req: TenantRequest, res: Response) => {
    try {
      const dateRange = parseDateRange(req.query);
      const estadisticas = await service.getEstadisticas(
        req.tenantId!,
        req.params.geocercaId,
        dateRange
      );
      res.json(estadisticas);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get summary statistics by unit
   * GET /api/gps/eventos-geocerca/resumen/unidad/:unidadId
   */
  router.get('/resumen/unidad/:unidadId', async (req: TenantRequest, res: Response) => {
    try {
      const dateRange = parseDateRange(req.query);

      // Get events for this unit
      const eventos = await service.findByUnidad(
        req.tenantId!,
        req.params.unidadId,
        dateRange
      );

      // Calculate summary statistics
      const geocercasVisitadas = new Set<string>();
      let totalEntradas = 0;
      let totalSalidas = 0;
      let totalPermanencias = 0;

      for (const evento of eventos) {
        geocercasVisitadas.add(evento.geocercaId);
        switch (evento.tipoEvento) {
          case TipoEventoGeocerca.ENTRADA:
            totalEntradas++;
            break;
          case TipoEventoGeocerca.SALIDA:
            totalSalidas++;
            break;
          case TipoEventoGeocerca.PERMANENCIA:
            totalPermanencias++;
            break;
        }
      }

      res.json({
        unidadId: req.params.unidadId,
        totalEventos: eventos.length,
        totalEntradas,
        totalSalidas,
        totalPermanencias,
        geocercasVisitadas: geocercasVisitadas.size,
        geocercaIds: Array.from(geocercasVisitadas),
        periodoInicio: dateRange.fechaInicio,
        periodoFin: dateRange.fechaFin,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get dwell time (permanence) statistics by geofence
   * GET /api/gps/eventos-geocerca/permanencia/geocerca/:geocercaId
   */
  router.get('/permanencia/geocerca/:geocercaId', async (req: TenantRequest, res: Response) => {
    try {
      const dateRange = parseDateRange(req.query);

      // Get events for this geofence
      const eventos = await service.findByGeocerca(
        req.tenantId!,
        req.params.geocercaId,
        dateRange
      );

      // Group events by unit and calculate dwell times
      const unidadEventos: Record<string, typeof eventos> = {};
      for (const evento of eventos) {
        if (!unidadEventos[evento.unidadId]) {
          unidadEventos[evento.unidadId] = [];
        }
        unidadEventos[evento.unidadId].push(evento);
      }

      const tiemposPorUnidad: Array<{
        unidadId: string;
        tiempoTotalMinutos: number;
        visitas: number;
        promedioMinutosPorVisita: number;
      }> = [];

      for (const [unidadId, eventosUnidad] of Object.entries(unidadEventos)) {
        const tiempoInfo = await service.getTiempoEnGeocerca(
          req.tenantId!,
          unidadId,
          req.params.geocercaId
        );
        tiemposPorUnidad.push({
          unidadId,
          tiempoTotalMinutos: tiempoInfo.tiempoTotalMinutos,
          visitas: tiempoInfo.visitasTotales,
          promedioMinutosPorVisita: tiempoInfo.promedioMinutosPorVisita,
        });
      }

      // Sort by total time descending
      tiemposPorUnidad.sort((a, b) => b.tiempoTotalMinutos - a.tiempoTotalMinutos);

      const tiempoTotalGeocerca = tiemposPorUnidad.reduce(
        (sum, t) => sum + t.tiempoTotalMinutos,
        0
      );
      const visitasTotales = tiemposPorUnidad.reduce((sum, t) => sum + t.visitas, 0);

      res.json({
        geocercaId: req.params.geocercaId,
        tiempoTotalMinutos: Math.round(tiempoTotalGeocerca * 100) / 100,
        visitasTotales,
        unidadesUnicas: tiemposPorUnidad.length,
        promedioMinutosPorVisita:
          visitasTotales > 0
            ? Math.round((tiempoTotalGeocerca / visitasTotales) * 100) / 100
            : 0,
        detallesPorUnidad: tiemposPorUnidad,
        periodoInicio: dateRange.fechaInicio,
        periodoFin: dateRange.fechaFin,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get events by geofence
   * GET /api/gps/eventos-geocerca/geocerca/:geocercaId
   */
  router.get('/geocerca/:geocercaId', async (req: TenantRequest, res: Response) => {
    try {
      const dateRange = parseDateRange(req.query);
      const eventos = await service.findByGeocerca(
        req.tenantId!,
        req.params.geocercaId,
        dateRange
      );
      res.json({
        data: eventos,
        total: eventos.length,
        geocercaId: req.params.geocercaId,
        fechaDesde: dateRange.fechaInicio,
        fechaHasta: dateRange.fechaFin,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get events by unit
   * GET /api/gps/eventos-geocerca/unidad/:unidadId
   */
  router.get('/unidad/:unidadId', async (req: TenantRequest, res: Response) => {
    try {
      const dateRange = parseDateRange(req.query);
      const eventos = await service.findByUnidad(
        req.tenantId!,
        req.params.unidadId,
        dateRange
      );
      res.json({
        data: eventos,
        total: eventos.length,
        unidadId: req.params.unidadId,
        fechaDesde: dateRange.fechaInicio,
        fechaHasta: dateRange.fechaFin,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get events by trip
   * GET /api/gps/eventos-geocerca/viaje/:viajeId
   */
  router.get('/viaje/:viajeId', async (req: TenantRequest, res: Response) => {
    try {
      const filters: EventoGeocercaFilters = {
        viajeId: req.params.viajeId,
      };

      const result = await service.findAll(req.tenantId!, filters, { page: 1, limit: 500 });
      res.json({
        data: result.data,
        total: result.total,
        viajeId: req.params.viajeId,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Register a new geofence event
   * POST /api/gps/eventos-geocerca
   */
  router.post('/', async (req: TenantRequest, res: Response) => {
    try {
      const evento = await service.create(req.tenantId!, {
        geocercaId: req.body.geocercaId,
        dispositivoId: req.body.dispositivoId,
        unidadId: req.body.unidadId,
        tipoEvento: req.body.tipoEvento,
        posicionId: req.body.posicionId,
        latitud: parseFloat(req.body.latitud),
        longitud: parseFloat(req.body.longitud),
        tiempoEvento: new Date(req.body.tiempoEvento),
        viajeId: req.body.viajeId,
        metadata: req.body.metadata,
      });
      res.status(201).json(evento);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * Trigger alert for an event
   * POST /api/gps/eventos-geocerca/:id/alerta
   */
  router.post('/:id/alerta', async (req: TenantRequest, res: Response) => {
    try {
      const evento = await service.triggerAlerta(req.tenantId!, req.params.id);
      if (!evento) {
        return res.status(404).json({ error: 'Evento de geocerca no encontrado' });
      }
      res.json(evento);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get units currently inside a geofence
   * GET /api/gps/eventos-geocerca/geocerca/:geocercaId/unidades-dentro
   */
  router.get('/geocerca/:geocercaId/unidades-dentro', async (req: TenantRequest, res: Response) => {
    try {
      const unidades = await service.getUnidadesEnGeocerca(
        req.tenantId!,
        req.params.geocercaId
      );
      res.json({
        data: unidades,
        total: unidades.length,
        geocercaId: req.params.geocercaId,
        timestamp: new Date(),
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get event by ID
   * GET /api/gps/eventos-geocerca/:id
   */
  router.get('/:id', async (req: TenantRequest, res: Response) => {
    try {
      const result = await service.findAll(
        req.tenantId!,
        {},
        { page: 1, limit: 1 }
      );

      // Find by ID in result
      const evento = result.data.find((e) => e.id === req.params.id);
      if (!evento) {
        return res.status(404).json({ error: 'Evento de geocerca no encontrado' });
      }
      res.json(evento);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Delete old events (data retention)
   * DELETE /api/gps/eventos-geocerca/antiguos
   */
  router.delete('/antiguos', async (req: TenantRequest, res: Response) => {
    try {
      const antesDe = new Date(req.query.antesDe as string);
      if (isNaN(antesDe.getTime())) {
        return res.status(400).json({ error: 'Fecha inválida. Use formato ISO 8601.' });
      }
      const eliminados = await service.eliminarEventosAntiguos(req.tenantId!, antesDe);
      res.json({ eliminados });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  return router;
}
