/**
 * Sync Controller
 * ERP Transportistas
 *
 * REST API endpoints for offline sync operations.
 * Sprint: S4 - TASK-007
 * Module: Offline Sync
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { SyncService } from '../services/sync.service';
import { TipoOperacionOffline, EstadoSincronizacion, PrioridadSync } from '../entities/offline-queue.entity';

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

export function createSyncController(dataSource: DataSource): Router {
  const router = Router();
  const service = new SyncService(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;
    req.dispositivoId = req.headers['x-dispositivo-id'] as string;
    next();
  };

  router.use(extractTenant);

  /**
   * Enqueue a single offline operation
   * POST /api/offline/encolar
   */
  router.post('/encolar', async (req: TenantRequest, res: Response) => {
    try {
      const {
        dispositivoId,
        usuarioId,
        unidadId,
        viajeId,
        tipoOperacion,
        prioridad,
        payload,
        endpointDestino,
        metodoHttp,
        creadoOfflineEn,
        clienteId,
      } = req.body;

      if (!tipoOperacion || !payload || !endpointDestino) {
        return res.status(400).json({
          error: 'tipoOperacion, payload y endpointDestino son requeridos',
        });
      }

      const operacion = await service.encolar(req.tenantId!, {
        dispositivoId: dispositivoId || req.dispositivoId,
        usuarioId: usuarioId || req.userId,
        unidadId,
        viajeId,
        tipoOperacion,
        prioridad,
        payload,
        endpointDestino,
        metodoHttp,
        creadoOfflineEn: creadoOfflineEn ? new Date(creadoOfflineEn) : new Date(),
        clienteId,
      });

      res.status(201).json({
        success: true,
        data: operacion,
      });
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * Enqueue multiple operations (batch)
   * POST /api/offline/encolar-lote
   */
  router.post('/encolar-lote', async (req: TenantRequest, res: Response) => {
    try {
      const { operaciones } = req.body;

      if (!Array.isArray(operaciones) || operaciones.length === 0) {
        return res.status(400).json({
          error: 'operaciones debe ser un array no vacío',
        });
      }

      if (operaciones.length > 100) {
        return res.status(400).json({
          error: 'Máximo 100 operaciones por lote',
        });
      }

      const resultados = await service.encolarLote(
        req.tenantId!,
        operaciones.map((op: any) => ({
          ...op,
          dispositivoId: op.dispositivoId || req.dispositivoId,
          usuarioId: op.usuarioId || req.userId,
          creadoOfflineEn: op.creadoOfflineEn ? new Date(op.creadoOfflineEn) : new Date(),
        }))
      );

      res.status(201).json({
        success: true,
        total: resultados.length,
        data: resultados,
      });
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * Get pending operations
   * GET /api/offline/pendientes
   */
  router.get('/pendientes', async (req: TenantRequest, res: Response) => {
    try {
      const limite = parseInt(req.query.limite as string, 10) || 50;
      const operaciones = await service.obtenerPendientes(req.tenantId!, Math.min(limite, 100));

      res.json({
        success: true,
        total: operaciones.length,
        data: operaciones,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get operations for a device
   * GET /api/offline/dispositivo/:dispositivoId
   */
  router.get('/dispositivo/:dispositivoId', async (req: TenantRequest, res: Response) => {
    try {
      const { dispositivoId } = req.params;
      const { estado, tipoOperacion, viajeId } = req.query;

      const operaciones = await service.obtenerPorDispositivo(req.tenantId!, dispositivoId, {
        estado: estado as EstadoSincronizacion,
        tipoOperacion: tipoOperacion as TipoOperacionOffline,
        viajeId: viajeId as string,
      });

      res.json({
        success: true,
        total: operaciones.length,
        data: operaciones,
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Process sync batch
   * POST /api/offline/sincronizar
   */
  router.post('/sincronizar', async (req: TenantRequest, res: Response) => {
    try {
      const limite = parseInt(req.body.limite as string, 10) || 20;
      const resultado = await service.procesarLote(req.tenantId!, Math.min(limite, 50));

      res.json({
        success: true,
        ...resultado,
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get queue statistics
   * GET /api/offline/estadisticas
   */
  router.get('/estadisticas', async (req: TenantRequest, res: Response) => {
    try {
      const estadisticas = await service.getEstadisticas(req.tenantId!);

      res.json({
        success: true,
        data: estadisticas,
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Retry failed operations
   * POST /api/offline/reintentar
   */
  router.post('/reintentar', async (req: TenantRequest, res: Response) => {
    try {
      const reintentados = await service.reintentarFallidos(req.tenantId!);

      res.json({
        success: true,
        reintentados,
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Clean up old completed operations
   * DELETE /api/offline/limpiar
   */
  router.delete('/limpiar', async (req: TenantRequest, res: Response) => {
    try {
      const diasAntiguedad = parseInt(req.query.dias as string, 10) || 7;
      const eliminados = await service.limpiarCompletados(req.tenantId!, diasAntiguedad);

      res.json({
        success: true,
        eliminados,
        diasAntiguedad,
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * Get enum values for client reference
   * GET /api/offline/tipos
   */
  router.get('/tipos', (_req: TenantRequest, res: Response) => {
    res.json({
      tiposOperacion: Object.values(TipoOperacionOffline),
      estadosSincronizacion: Object.values(EstadoSincronizacion),
      prioridades: Object.values(PrioridadSync),
    });
  });

  return router;
}
