/**
 * ProtocoloCalidadController - REST API for quality protocols (Protocolos de Calidad)
 *
 * Wrapper controller que expone checklists como protocolos de calidad con endpoints en espanol.
 * Un protocolo de calidad es esencialmente un checklist con su configuracion de items.
 *
 * @module Quality (MAI-009)
 * @routes /api/protocolos-calidad
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import {
  ChecklistService,
  CreateChecklistDto,
  UpdateChecklistDto,
  ChecklistFilters,
} from '../services/checklist.service';
import { Checklist, ChecklistStage } from '../entities/checklist.entity';
import { ChecklistItem } from '../entities/checklist-item.entity';
import { AuthMiddleware } from '../../auth/middleware/auth.middleware';
import { AuthService } from '../../auth/services/auth.service';
import { User } from '../../core/entities/user.entity';
import { Tenant } from '../../core/entities/tenant.entity';
import { RefreshToken } from '../../auth/entities/refresh-token.entity';

interface ServiceContext {
  tenantId: string;
  userId?: string;
}

/**
 * Creates the ProtocoloCalidad controller with all REST endpoints
 * @param dataSource - TypeORM DataSource for database operations
 * @returns Express Router with all endpoints configured
 */
export function createProtocoloCalidadController(dataSource: DataSource): Router {
  const router = Router();

  // Repositories
  const checklistRepo = dataSource.getRepository(Checklist);
  const itemRepo = dataSource.getRepository(ChecklistItem);
  const userRepository = dataSource.getRepository(User);
  const tenantRepository = dataSource.getRepository(Tenant);
  const refreshTokenRepository = dataSource.getRepository(RefreshToken);

  // Services
  const service = new ChecklistService(checklistRepo, itemRepo);
  const authService = new AuthService(userRepository, tenantRepository, refreshTokenRepository as any);
  const authMiddleware = new AuthMiddleware(authService, dataSource);

  /**
   * Helper to extract service context from request
   */
  const getContext = (req: Request): ServiceContext => {
    if (!req.tenantId) {
      throw new Error('Tenant ID is required');
    }
    return {
      tenantId: req.tenantId,
      userId: req.user?.sub,
    };
  };

  /**
   * Valid stages for quality protocols
   */
  const VALID_STAGES: ChecklistStage[] = ['foundation', 'structure', 'installations', 'finishes', 'delivery', 'custom'];

  /**
   * GET /api/protocolos-calidad
   * List quality protocols (checklists) with filters and pagination
   *
   * Query params:
   * - stage: Filter by construction stage
   * - prototypeId: Filter by prototype
   * - isActive: Filter active/inactive protocols
   * - search: Search in name, code, description
   * - page: Page number (default: 1)
   * - limit: Items per page (default: 20)
   */
  router.get('/', 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 filters: ChecklistFilters = {};
      if (req.query.stage) filters.stage = req.query.stage as ChecklistStage;
      if (req.query.prototypeId) filters.prototypeId = req.query.prototypeId as string;
      if (req.query.isActive !== undefined) filters.isActive = req.query.isActive === 'true';
      if (req.query.search) filters.search = req.query.search as string;
      filters.page = parseInt(req.query.page as string) || 1;
      filters.limit = Math.min(parseInt(req.query.limit as string) || 20, 100);

      const result = await service.findAll(getContext(req), filters);

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

  /**
   * GET /api/protocolos-calidad/estadisticas
   * Get statistics for quality protocols
   */
  router.get('/estadisticas', 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 stats = await service.getStatistics(getContext(req));

      res.status(200).json({
        success: true,
        data: stats,
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /api/protocolos-calidad/por-etapa/:stage
   * Get protocols by construction stage
   */
  router.get('/por-etapa/:stage', 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 stage = req.params.stage as ChecklistStage;
      if (!VALID_STAGES.includes(stage)) {
        res.status(400).json({
          error: 'Bad Request',
          message: `Etapa invalida. Valores validos: ${VALID_STAGES.join(', ')}`,
        });
        return;
      }

      const protocols = await service.findByStage(getContext(req), stage);

      res.status(200).json({
        success: true,
        data: protocols,
        count: protocols.length,
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /api/protocolos-calidad/por-codigo/:code
   * Get protocol by code
   */
  router.get('/por-codigo/:code', 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 protocol = await service.findByCode(getContext(req), req.params.code);
      if (!protocol) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        data: protocol,
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /api/protocolos-calidad/:id
   * Get protocol detail with checklist items
   */
  router.get('/:id', 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 protocol = await service.findWithItems(getContext(req), req.params.id);
      if (!protocol) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        data: protocol,
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /api/protocolos-calidad
   * Create new quality protocol
   *
   * Body:
   * - code: (required) Unique protocol code
   * - name: (required) Protocol name
   * - stage: (required) Construction stage
   * - description: (optional) Protocol description
   * - prototypeId: (optional) Associated prototype UUID
   */
  router.post('/', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 { code, name, stage, description, prototypeId } = req.body;
      if (!code || !name || !stage) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'code, name y stage son requeridos',
        });
        return;
      }

      if (!VALID_STAGES.includes(stage)) {
        res.status(400).json({
          error: 'Bad Request',
          message: `Etapa invalida. Valores validos: ${VALID_STAGES.join(', ')}`,
        });
        return;
      }

      const dto: CreateChecklistDto = {
        code,
        name,
        stage,
        description,
        prototypeId,
      };

      const protocol = await service.create(getContext(req), dto);

      res.status(201).json({
        success: true,
        data: protocol,
        message: 'Protocolo de calidad creado exitosamente',
      });
    } catch (error: any) {
      if (error.message?.includes('already exists')) {
        res.status(409).json({ error: 'Conflict', message: error.message });
        return;
      }
      next(error);
    }
  });

  /**
   * PUT /api/protocolos-calidad/:id
   * Update quality protocol
   */
  router.put('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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: UpdateChecklistDto = {};
      if (req.body.name !== undefined) dto.name = req.body.name;
      if (req.body.description !== undefined) dto.description = req.body.description;
      if (req.body.stage !== undefined) {
        if (!VALID_STAGES.includes(req.body.stage)) {
          res.status(400).json({
            error: 'Bad Request',
            message: `Etapa invalida. Valores validos: ${VALID_STAGES.join(', ')}`,
          });
          return;
        }
        dto.stage = req.body.stage;
      }
      if (req.body.prototypeId !== undefined) dto.prototypeId = req.body.prototypeId;
      if (req.body.isActive !== undefined) dto.isActive = req.body.isActive;

      const protocol = await service.update(getContext(req), req.params.id, dto);
      if (!protocol) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        data: protocol,
        message: 'Protocolo actualizado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /api/protocolos-calidad/:id/activar
   * Activate quality protocol
   */
  router.post('/:id/activar', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 protocol = await service.activate(getContext(req), req.params.id);
      if (!protocol) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        data: protocol,
        message: 'Protocolo activado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /api/protocolos-calidad/:id/desactivar
   * Deactivate quality protocol
   */
  router.post('/:id/desactivar', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 protocol = await service.deactivate(getContext(req), req.params.id);
      if (!protocol) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        data: protocol,
        message: 'Protocolo desactivado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /api/protocolos-calidad/:id/duplicar
   * Duplicate quality protocol
   *
   * Body:
   * - newCode: (required) Code for the new protocol
   * - newName: (required) Name for the new protocol
   */
  router.post('/:id/duplicar', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 { newCode, newName } = req.body;
      if (!newCode || !newName) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'newCode y newName son requeridos',
        });
        return;
      }

      const protocol = await service.duplicate(getContext(req), req.params.id, newCode, newName);
      if (!protocol) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(201).json({
        success: true,
        data: protocol,
        message: 'Protocolo duplicado exitosamente',
      });
    } catch (error: any) {
      if (error.message?.includes('already exists')) {
        res.status(409).json({ error: 'Conflict', message: error.message });
        return;
      }
      next(error);
    }
  });

  /**
   * DELETE /api/protocolos-calidad/:id
   * Soft delete quality protocol
   */
  router.delete('/:id', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 deleted = await service.softDelete(getContext(req), req.params.id);
      if (!deleted) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        message: 'Protocolo eliminado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  // ─────────────────────────────────────────────────────────────────────────────
  // ITEMS ENDPOINTS (Checklist items as protocol criteria)
  // ─────────────────────────────────────────────────────────────────────────────

  /**
   * POST /api/protocolos-calidad/:id/criterios
   * Add criterion (checklist item) to protocol
   *
   * Body:
   * - sequenceNumber: (required) Order number
   * - category: (required) Category/section name
   * - description: (required) Criterion description
   * - isCritical: (optional) Is this a critical criterion
   * - requiresPhoto: (optional) Does this require photo evidence
   * - acceptanceCriteria: (optional) Acceptance criteria text
   */
  router.post('/:id/criterios', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 { sequenceNumber, category, description, isCritical, requiresPhoto, acceptanceCriteria } = req.body;
      if (sequenceNumber === undefined || !category || !description) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'sequenceNumber, category y description son requeridos',
        });
        return;
      }

      const item = await service.addItem(getContext(req), req.params.id, {
        sequenceNumber,
        category,
        description,
        isCritical,
        requiresPhoto,
        acceptanceCriteria,
      });

      res.status(201).json({
        success: true,
        data: item,
        message: 'Criterio agregado exitosamente',
      });
    } catch (error: any) {
      if (error.message?.includes('not found')) {
        res.status(404).json({ error: 'Not Found', message: error.message });
        return;
      }
      next(error);
    }
  });

  /**
   * PATCH /api/protocolos-calidad/:protocolId/criterios/:itemId
   * Update criterion in protocol
   */
  router.patch('/:protocolId/criterios/:itemId', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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: any = {};
      if (req.body.sequenceNumber !== undefined) dto.sequenceNumber = req.body.sequenceNumber;
      if (req.body.category !== undefined) dto.category = req.body.category;
      if (req.body.description !== undefined) dto.description = req.body.description;
      if (req.body.isCritical !== undefined) dto.isCritical = req.body.isCritical;
      if (req.body.requiresPhoto !== undefined) dto.requiresPhoto = req.body.requiresPhoto;
      if (req.body.acceptanceCriteria !== undefined) dto.acceptanceCriteria = req.body.acceptanceCriteria;
      if (req.body.isActive !== undefined) dto.isActive = req.body.isActive;

      const item = await service.updateItem(getContext(req), req.params.itemId, dto);
      if (!item) {
        res.status(404).json({ error: 'Not Found', message: 'Criterio no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        data: item,
        message: 'Criterio actualizado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * DELETE /api/protocolos-calidad/:protocolId/criterios/:itemId
   * Remove criterion from protocol
   */
  router.delete('/:protocolId/criterios/:itemId', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 deleted = await service.removeItem(getContext(req), req.params.itemId);
      if (!deleted) {
        res.status(404).json({ error: 'Not Found', message: 'Criterio no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        message: 'Criterio eliminado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /api/protocolos-calidad/:id/criterios/reordenar
   * Reorder criteria in protocol
   *
   * Body:
   * - itemOrders: Array of { itemId: string, sequenceNumber: number }
   */
  router.post('/:id/criterios/reordenar', authMiddleware.authenticate, authMiddleware.authorize('admin', 'quality'), 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 { itemOrders } = req.body;
      if (!itemOrders || !Array.isArray(itemOrders)) {
        res.status(400).json({
          error: 'Bad Request',
          message: 'itemOrders es requerido y debe ser un array',
        });
        return;
      }

      const success = await service.reorderItems(getContext(req), req.params.id, itemOrders);
      if (!success) {
        res.status(404).json({ error: 'Not Found', message: 'Protocolo no encontrado' });
        return;
      }

      res.status(200).json({
        success: true,
        message: 'Criterios reordenados exitosamente',
      });
    } catch (error) {
      next(error);
    }
  });

  return router;
}
