/**
 * EppController - Controller de EPP HSE
 *
 * Endpoints REST para gestión de equipo de protección personal.
 *
 * @module HSE
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  EppService,
  CreateEppCatalogoDto,
  CreateAsignacionDto,
  CreateInspeccionEppDto,
  CreateBajaDto,
  EppFilters,
  AsignacionFilters,
} from '../services/epp.service';
import { AuthMiddleware } from '../../auth/middleware/auth.middleware';
import { AuthService } from '../../auth/services/auth.service';
import { EppCatalogo } from '../entities/epp-catalogo.entity';
import { EppAsignacion } from '../entities/epp-asignacion.entity';
import { EppInspeccion } from '../entities/epp-inspeccion.entity';
import { EppBaja } from '../entities/epp-baja.entity';
import { EppMatrizPuesto } from '../entities/epp-matriz-puesto.entity';
import { EppInventario } from '../entities/epp-inventario.entity';
import { EppMovimiento } from '../entities/epp-movimiento.entity';
import { User } from '../../core/entities/user.entity';
import { Tenant } from '../../core/entities/tenant.entity';
import { RefreshToken } from '../../auth/entities/refresh-token.entity';

/**
 * Service context for multi-tenant operations
 */
interface ServiceContext {
  tenantId: string;
  userId?: string;
}

/**
 * Crear router de EPP
 */
export function createEppController(dataSource: DataSource): Router {
  const router = Router();

  // Repositorios
  const catalogoRepository = dataSource.getRepository(EppCatalogo);
  const asignacionRepository = dataSource.getRepository(EppAsignacion);
  const inspeccionRepository = dataSource.getRepository(EppInspeccion);
  const bajaRepository = dataSource.getRepository(EppBaja);
  const matrizRepository = dataSource.getRepository(EppMatrizPuesto);
  const inventarioRepository = dataSource.getRepository(EppInventario);
  const movimientoRepository = dataSource.getRepository(EppMovimiento);
  const userRepository = dataSource.getRepository(User);
  const tenantRepository = dataSource.getRepository(Tenant);
  const refreshTokenRepository = dataSource.getRepository(RefreshToken);

  // Servicios
  const eppService = new EppService(
    catalogoRepository,
    asignacionRepository,
    inspeccionRepository,
    bajaRepository,
    matrizRepository,
    inventarioRepository,
    movimientoRepository
  );
  const authService = new AuthService(userRepository, tenantRepository, refreshTokenRepository as any);
  const authMiddleware = new AuthMiddleware(authService, dataSource);

  // Helper para crear contexto de servicio
  const getContext = (req: Request): ServiceContext => {
    if (!req.tenantId) {
      throw new Error('Tenant ID is required');
    }
    return {
      tenantId: req.tenantId,
      userId: req.user?.sub,
    };
  };

  // ========== Catálogo EPP ==========

  /**
   * GET /epp/catalogo
   * Listar catálogo de EPP
   */
  router.get('/catalogo', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const tenantId = req.tenantId;
      if (!tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const page = parseInt(req.query.page as string) || 1;
      const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);

      const filters: EppFilters = {
        categoria: req.query.categoria as any,
        activo: req.query.activo === 'true' ? true : req.query.activo === 'false' ? false : undefined,
        search: req.query.search as string,
      };

      const result = await eppService.findCatalogo(getContext(req), filters, page, limit);

      res.status(200).json({
        success: true,
        data: result.data,
        pagination: {
          total: result.total,
          page: result.page,
          limit: result.limit,
          totalPages: result.totalPages,
        },
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /epp/catalogo/:id
   * Obtener EPP del catálogo por ID
   */
  router.get('/catalogo/:id', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const epp = await eppService.findCatalogoById(getContext(req), req.params.id);
      if (!epp) {
        res.status(404).json({ error: 'Not Found', message: 'EPP not found' });
        return;
      }
      res.status(200).json({ success: true, data: epp });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /epp/catalogo
   * Crear EPP en catálogo
   */
  router.post('/catalogo', authMiddleware.authenticate, authMiddleware.authorize('admin'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const tenantId = req.tenantId;
      if (!tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const dto: CreateEppCatalogoDto = req.body;

      if (!dto.codigo || !dto.nombre || !dto.categoria || !dto.vidaUtilDias) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'codigo, nombre, categoria and vidaUtilDias are required',
        });
        return;
      }

      const epp = await eppService.createCatalogo(getContext(req), dto);
      res.status(201).json({ success: true, data: epp });
    } catch (error) {
      next(error);
    }
  });

  // ========== Matriz por Puesto ==========

  /**
   * GET /epp/matriz/:puestoId
   * Obtener matriz de EPP por puesto
   */
  router.get('/matriz/:puestoId', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const matriz = await eppService.getMatrizByPuesto(getContext(req), req.params.puestoId);
      res.status(200).json({ success: true, data: matriz });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /epp/matriz/:puestoId
   * Configurar EPP requerido para puesto
   */
  router.post('/matriz/:puestoId', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const { eppId, esObligatorio, actividadEspecifica } = req.body;

      if (!eppId || esObligatorio === undefined) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'eppId and esObligatorio are required',
        });
        return;
      }

      const matriz = await eppService.setMatrizPuesto(
        getContext(req),
        req.params.puestoId,
        eppId,
        esObligatorio,
        actividadEspecifica
      );
      res.status(200).json({ success: true, data: matriz });
    } catch (error) {
      next(error);
    }
  });

  // ========== Asignaciones ==========

  /**
   * GET /epp/asignaciones
   * Listar asignaciones de EPP
   */
  router.get('/asignaciones', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const tenantId = req.tenantId;
      if (!tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const page = parseInt(req.query.page as string) || 1;
      const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);

      const filters: AsignacionFilters = {
        employeeId: req.query.employeeId as string,
        eppId: req.query.eppId as string,
        estado: req.query.estado as any,
        fraccionamientoId: req.query.fraccionamientoId as string,
        vencidos: req.query.vencidos === 'true',
      };

      const result = await eppService.findAsignaciones(getContext(req), filters, page, limit);

      res.status(200).json({
        success: true,
        data: result.data,
        pagination: {
          total: result.total,
          page: result.page,
          limit: result.limit,
          totalPages: result.totalPages,
        },
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /epp/asignaciones/:id
   * Obtener asignación por ID
   */
  router.get('/asignaciones/:id', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const asignacion = await eppService.findAsignacionById(getContext(req), req.params.id);
      if (!asignacion) {
        res.status(404).json({ error: 'Not Found', message: 'Assignment not found' });
        return;
      }
      res.status(200).json({ success: true, data: asignacion });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /epp/asignaciones
   * Crear asignación de EPP
   */
  router.post('/asignaciones', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer', 'resident'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const tenantId = req.tenantId;
      if (!tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const dto: CreateAsignacionDto = req.body;

      if (!dto.eppId || !dto.employeeId || !dto.fechaEntrega || !dto.fechaVencimiento) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'eppId, employeeId, fechaEntrega and fechaVencimiento are required',
        });
        return;
      }

      dto.fechaEntrega = new Date(dto.fechaEntrega);
      dto.fechaVencimiento = new Date(dto.fechaVencimiento);
      const asignacion = await eppService.createAsignacion(getContext(req), dto);
      res.status(201).json({ success: true, data: asignacion });
    } catch (error) {
      next(error);
    }
  });

  /**
   * PATCH /epp/asignaciones/:id/estado
   * Actualizar estado de asignación
   */
  router.patch('/asignaciones/:id/estado', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const { estado } = req.body;
      if (!estado) {
        res.status(400).json({ error: 'Bad Request', message: 'estado is required' });
        return;
      }

      const asignacion = await eppService.updateAsignacionEstado(getContext(req), req.params.id, estado);
      if (!asignacion) {
        res.status(404).json({ error: 'Not Found', message: 'Assignment not found' });
        return;
      }
      res.status(200).json({ success: true, data: asignacion });
    } catch (error) {
      next(error);
    }
  });

  // ========== Inspecciones EPP ==========

  /**
   * GET /epp/asignaciones/:id/inspecciones
   * Listar inspecciones de una asignación
   */
  router.get('/asignaciones/:id/inspecciones', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const inspecciones = await eppService.getInspeccionesByAsignacion(getContext(req), req.params.id);
      res.status(200).json({ success: true, data: inspecciones });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /epp/asignaciones/:id/inspecciones
   * Crear inspección de EPP
   */
  router.post('/asignaciones/:id/inspecciones', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer', 'resident'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const dto: CreateInspeccionEppDto = {
        ...req.body,
        asignacionId: req.params.id,
      };

      if (!dto.inspectorId || !dto.estadoEpp) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'inspectorId and estadoEpp are required',
        });
        return;
      }

      const inspeccion = await eppService.createInspeccion(getContext(req), dto);
      res.status(201).json({ success: true, data: inspeccion });
    } catch (error) {
      if (error instanceof Error && error.message === 'Asignación no encontrada') {
        res.status(404).json({ error: 'Not Found', message: error.message });
        return;
      }
      next(error);
    }
  });

  // ========== Bajas ==========

  /**
   * POST /epp/asignaciones/:id/baja
   * Dar de baja EPP
   */
  router.post('/asignaciones/:id/baja', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const dto: CreateBajaDto = {
        ...req.body,
        asignacionId: req.params.id,
      };

      if (!dto.motivo) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'motivo is required',
        });
        return;
      }

      const baja = await eppService.createBaja(getContext(req), dto);
      res.status(201).json({ success: true, data: baja, message: 'EPP given off' });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === 'Asignación no encontrada') {
          res.status(404).json({ error: 'Not Found', message: error.message });
          return;
        }
        if (error.message === 'EPP ya fue dado de baja') {
          res.status(400).json({ error: 'Bad Request', message: error.message });
          return;
        }
      }
      next(error);
    }
  });

  // ========== Inventario ==========

  /**
   * GET /epp/inventario
   * Obtener inventario de EPP
   */
  router.get('/inventario', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const almacenId = req.query.almacenId as string;
      const inventario = await eppService.getInventario(getContext(req), almacenId);
      res.status(200).json({ success: true, data: inventario });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /epp/movimientos
   * Registrar movimiento de inventario
   */
  router.post('/movimientos', authMiddleware.authenticate, authMiddleware.authorize('admin', 'engineer'), async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const { eppId, tipo, cantidad, almacenOrigenId, almacenDestinoId, referencia } = req.body;

      if (!eppId || !tipo || !cantidad) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'eppId, tipo and cantidad are required',
        });
        return;
      }

      const movimiento = await eppService.registrarMovimiento(
        getContext(req),
        eppId,
        tipo,
        cantidad,
        almacenOrigenId,
        almacenDestinoId,
        referencia
      );
      res.status(201).json({ success: true, data: movimiento });
    } catch (error) {
      next(error);
    }
  });

  // ========== Estadísticas ==========

  /**
   * GET /epp/stats
   * Obtener estadísticas de EPP
   */
  router.get('/stats', authMiddleware.authenticate, async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const tenantId = req.tenantId;
      if (!tenantId) {
        res.status(400).json({ error: 'Bad Request', message: 'Tenant ID required' });
        return;
      }

      const fraccionamientoId = req.query.fraccionamientoId as string;
      const stats = await eppService.getStats(getContext(req), fraccionamientoId);
      res.status(200).json({ success: true, data: stats });
    } catch (error) {
      next(error);
    }
  });

  return router;
}

export default createEppController;
