import { Request, Response, NextFunction, Router } from 'express';
import { InventoryService } from '../services/inventory.service';
import {
  CreateStockMovementDto,
  AdjustStockDto,
  TransferStockDto,
  ReserveStockDto,
} from '../dto';

export class InventoryController {
  public router: Router;

  constructor(private readonly inventoryService: InventoryService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Stock Levels
    this.router.get('/stock', this.getStockLevels.bind(this));
    this.router.get('/stock/product/:productId', this.getStockByProduct.bind(this));
    this.router.get('/stock/warehouse/:warehouseId', this.getStockByWarehouse.bind(this));
    this.router.get(
      '/stock/available/:productId/:warehouseId',
      this.getAvailableStock.bind(this)
    );

    // Movements
    this.router.get('/movements', this.getMovements.bind(this));
    this.router.get('/movements/:id', this.getMovement.bind(this));
    this.router.post('/movements', this.createMovement.bind(this));
    this.router.post('/movements/:id/confirm', this.confirmMovement.bind(this));
    this.router.post('/movements/:id/cancel', this.cancelMovement.bind(this));

    // Operations
    this.router.post('/adjust', this.adjustStock.bind(this));
    this.router.post('/transfer', this.transferStock.bind(this));
    this.router.post('/reserve', this.reserveStock.bind(this));
    this.router.post('/release', this.releaseReservation.bind(this));
  }

  // ==================== Stock Levels ====================

  private async getStockLevels(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const {
        productId,
        warehouseId,
        locationId,
        lotNumber,
        hasStock,
        lowStock,
        limit,
        offset,
      } = req.query;

      const result = await this.inventoryService.getStockLevels({
        tenantId,
        productId: productId as string,
        warehouseId: warehouseId as string,
        locationId: locationId as string,
        lotNumber: lotNumber as string,
        hasStock: hasStock ? hasStock === 'true' : undefined,
        lowStock: lowStock ? lowStock === 'true' : undefined,
        limit: limit ? parseInt(limit as string, 10) : undefined,
        offset: offset ? parseInt(offset as string, 10) : undefined,
      });

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

  private async getStockByProduct(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { productId } = req.params;
      const stock = await this.inventoryService.getStockByProduct(productId, tenantId);
      res.json({ data: stock });
    } catch (error) {
      next(error);
    }
  }

  private async getStockByWarehouse(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { warehouseId } = req.params;
      const stock = await this.inventoryService.getStockByWarehouse(warehouseId, tenantId);
      res.json({ data: stock });
    } catch (error) {
      next(error);
    }
  }

  private async getAvailableStock(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { productId, warehouseId } = req.params;
      const available = await this.inventoryService.getAvailableStock(
        productId,
        warehouseId,
        tenantId
      );
      res.json({ data: { available } });
    } catch (error) {
      next(error);
    }
  }

  // ==================== Movements ====================

  private async getMovements(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const {
        movementType,
        productId,
        warehouseId,
        status,
        referenceType,
        referenceId,
        fromDate,
        toDate,
        limit,
        offset,
      } = req.query;

      const result = await this.inventoryService.getMovements({
        tenantId,
        movementType: movementType as string,
        productId: productId as string,
        warehouseId: warehouseId as string,
        status: status as 'draft' | 'confirmed' | 'cancelled',
        referenceType: referenceType as string,
        referenceId: referenceId as string,
        fromDate: fromDate ? new Date(fromDate as string) : undefined,
        toDate: toDate ? new Date(toDate as string) : undefined,
        limit: limit ? parseInt(limit as string, 10) : undefined,
        offset: offset ? parseInt(offset as string, 10) : undefined,
      });

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

  private async getMovement(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const movement = await this.inventoryService.getMovement(id, tenantId);

      if (!movement) {
        res.status(404).json({ error: 'Movement not found' });
        return;
      }

      res.json({ data: movement });
    } catch (error) {
      next(error);
    }
  }

  private async createMovement(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const dto: CreateStockMovementDto = req.body;
      const movement = await this.inventoryService.createMovement(tenantId, dto, userId);
      res.status(201).json({ data: movement });
    } catch (error) {
      next(error);
    }
  }

  private async confirmMovement(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const movement = await this.inventoryService.confirmMovement(id, tenantId, userId);

      if (!movement) {
        res.status(404).json({ error: 'Movement not found' });
        return;
      }

      res.json({ data: movement });
    } catch (error) {
      next(error);
    }
  }

  private async cancelMovement(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const movement = await this.inventoryService.cancelMovement(id, tenantId);

      if (!movement) {
        res.status(404).json({ error: 'Movement not found' });
        return;
      }

      res.json({ data: movement });
    } catch (error) {
      next(error);
    }
  }

  // ==================== Operations ====================

  private async adjustStock(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const dto: AdjustStockDto = req.body;
      const movement = await this.inventoryService.adjustStock(tenantId, dto, userId);
      res.status(201).json({ data: movement });
    } catch (error) {
      next(error);
    }
  }

  private async transferStock(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const dto: TransferStockDto = req.body;
      const movement = await this.inventoryService.transferStock(tenantId, dto, userId);
      res.status(201).json({ data: movement });
    } catch (error) {
      next(error);
    }
  }

  private async reserveStock(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const dto: ReserveStockDto = req.body;
      await this.inventoryService.reserveStock(tenantId, dto);
      res.json({ success: true });
    } catch (error) {
      next(error);
    }
  }

  private async releaseReservation(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { productId, warehouseId, quantity } = req.body;
      await this.inventoryService.releaseReservation(productId, warehouseId, quantity, tenantId);
      res.json({ success: true });
    } catch (error) {
      next(error);
    }
  }
}
