/**
 * WarehouseLocationController - Controlador de Ubicaciones de Almacen
 *
 * Endpoints para gestion de ubicaciones dentro de almacenes.
 * Las ubicaciones representan posiciones fisicas (pasillos, racks, estantes, bins).
 *
 * @module Warehouses
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { WarehouseLocationService, ServiceContext } from '../services';

/**
 * Extract service context from request
 */
function getServiceContext(req: Request): ServiceContext {
  return {
    tenantId: req.headers['x-tenant-id'] as string,
    userId: (req as any).user?.id,
    companyId: req.headers['x-company-id'] as string,
  };
}

export function createWarehouseLocationController(dataSource: DataSource): Router {
  const router = Router();
  const service = new WarehouseLocationService(dataSource);

  // ==================== CRUD DE UBICACIONES ====================

  /**
   * GET /
   * Lista ubicaciones con filtros y paginacion
   */
  router.get('/', async (req: Request, res: Response, _next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const filters = {
        warehouseId: req.query.warehouseId as string,
        parentId: req.query.parentId as string,
        locationType: req.query.locationType as any,
        isActive: req.query.isActive === 'true' ? true : req.query.isActive === 'false' ? false : undefined,
        isPickable: req.query.isPickable === 'true' ? true : req.query.isPickable === 'false' ? false : undefined,
        isReceivable: req.query.isReceivable === 'true' ? true : req.query.isReceivable === 'false' ? false : undefined,
        search: req.query.search as string,
      };

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

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

  /**
   * GET /search
   * Busqueda de ubicaciones para autocomplete
   */
  router.get('/search', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);
      const query = req.query.q as string;
      const warehouseId = req.query.warehouseId as string;
      const limit = parseInt(req.query.limit as string) || 10;

      if (!query) {
        res.status(400).json({ error: 'Se requiere parametro de busqueda (q)' });
        return;
      }

      const locations = await service.search(ctx, query, warehouseId, limit);
      res.json(locations);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-barcode/:barcode
   * Obtiene una ubicacion por codigo de barras
   */
  router.get('/by-barcode/:barcode', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.findByBarcode(ctx, req.params.barcode);
      if (!location) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.json(location);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId
   * Lista ubicaciones de un almacen especifico
   */
  router.get('/by-warehouse/:warehouseId', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const filters = {
        parentId: req.query.parentId as string,
        locationType: req.query.locationType as any,
        isActive: req.query.isActive === 'true' ? true : req.query.isActive === 'false' ? false : undefined,
        isPickable: req.query.isPickable === 'true' ? true : undefined,
        isReceivable: req.query.isReceivable === 'true' ? true : undefined,
        search: req.query.search as string,
      };

      const locations = await service.findByWarehouse(ctx, req.params.warehouseId, filters);
      res.json(locations);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/tree
   * Obtiene el arbol de ubicaciones de un almacen
   */
  router.get('/by-warehouse/:warehouseId/tree', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const locations = await service.getTree(ctx, req.params.warehouseId);
      res.json(locations);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/root
   * Obtiene las ubicaciones raiz (sin padre) de un almacen
   */
  router.get('/by-warehouse/:warehouseId/root', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const locations = await service.getRootLocations(ctx, req.params.warehouseId);
      res.json(locations);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/pickable
   * Lista ubicaciones de picking de un almacen
   */
  router.get('/by-warehouse/:warehouseId/pickable', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const locations = await service.getPickableLocations(ctx, req.params.warehouseId);
      res.json(locations);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/receivable
   * Lista ubicaciones de recepcion de un almacen
   */
  router.get('/by-warehouse/:warehouseId/receivable', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const locations = await service.getReceivableLocations(ctx, req.params.warehouseId);
      res.json(locations);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/statistics
   * Obtiene estadisticas de ubicaciones de un almacen
   */
  router.get('/by-warehouse/:warehouseId/statistics', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const stats = await service.getStatistics(ctx, req.params.warehouseId);
      res.json(stats);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/check-code/:code
   * Verifica si un codigo de ubicacion esta disponible
   */
  router.get('/by-warehouse/:warehouseId/check-code/:code', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);
      const excludeId = req.query.excludeId as string;

      const available = await service.isCodeAvailable(
        ctx,
        req.params.warehouseId,
        req.params.code,
        excludeId
      );
      res.json({ available });
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /by-warehouse/:warehouseId/by-code/:code
   * Obtiene una ubicacion por codigo dentro de un almacen
   */
  router.get('/by-warehouse/:warehouseId/by-code/:code', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.findByCode(ctx, req.params.warehouseId, req.params.code);
      if (!location) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.json(location);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /:id
   * Obtiene una ubicacion por ID
   */
  router.get('/:id', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.findById(ctx, req.params.id);
      if (!location) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.json(location);
    } catch (error) {
      next(error);
    }
  });

  /**
   * GET /:id/children
   * Obtiene las ubicaciones hijas de una ubicacion
   */
  router.get('/:id/children', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const children = await service.getChildren(ctx, req.params.id);
      res.json(children);
    } catch (error) {
      next(error);
    }
  });

  /**
   * POST /
   * Crea una nueva ubicacion
   */
  router.post('/', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.create(ctx, req.body);
      res.status(201).json(location);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /by-warehouse/:warehouseId/bulk
   * Crea multiples ubicaciones para un almacen
   */
  router.post('/by-warehouse/:warehouseId/bulk', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      if (!Array.isArray(req.body.locations)) {
        res.status(400).json({ error: 'Se requiere un array de ubicaciones en el campo "locations"' });
        return;
      }

      const locations = await service.bulkCreate(ctx, req.params.warehouseId, req.body.locations);
      res.status(201).json(locations);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /generate-code
   * Genera un codigo de ubicacion basado en coordenadas
   */
  router.post('/generate-code', async (req: Request, res: Response): Promise<void> => {
    try {
      const { aisle, rack, shelf, bin } = req.body;
      const code = service.generateLocationCode(aisle, rack, shelf, bin);
      res.json({ code });
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * PUT /:id
   * Actualiza una ubicacion
   */
  router.put('/:id', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.update(ctx, req.params.id, req.body);
      if (!location) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.json(location);
    } catch (error) {
      next(error);
    }
  });

  /**
   * PATCH /:id/activate
   * Activa una ubicacion
   */
  router.patch('/:id/activate', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.setActive(ctx, req.params.id, true);
      if (!location) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.json(location);
    } catch (error) {
      next(error);
    }
  });

  /**
   * PATCH /:id/deactivate
   * Desactiva una ubicacion
   */
  router.patch('/:id/deactivate', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const location = await service.setActive(ctx, req.params.id, false);
      if (!location) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.json(location);
    } catch (error) {
      next(error);
    }
  });

  /**
   * DELETE /:id
   * Elimina una ubicacion (soft delete)
   */
  router.delete('/:id', async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const ctx = getServiceContext(req);

      const deleted = await service.delete(ctx, req.params.id);
      if (!deleted) {
        res.status(404).json({ error: 'Ubicacion no encontrada' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      if ((error as Error).message.includes('child locations')) {
        res.status(400).json({ error: (error as Error).message });
        return;
      }
      next(error);
    }
  });

  return router;
}
