import { Request, Response, NextFunction, Router } from 'express';
import { WarehousesService } from '../services/warehouses.service';
import {
  CreateWarehouseDto,
  UpdateWarehouseDto,
  CreateWarehouseLocationDto,
  UpdateWarehouseLocationDto,
} from '../dto';

export class WarehousesController {
  public router: Router;

  constructor(private readonly warehousesService: WarehousesService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Warehouses
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/active', this.getActiveWarehouses.bind(this));
    this.router.get('/default', this.getDefaultWarehouse.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.get('/code/:code', this.findByCode.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.patch('/:id', this.update.bind(this));
    this.router.delete('/:id', this.delete.bind(this));

    // Locations
    this.router.get('/:id/locations', this.getLocations.bind(this));
    this.router.get('/:id/locations/tree', this.getLocationTree.bind(this));
    this.router.get('/:id/locations/pickable', this.getPickableLocations.bind(this));
    this.router.get('/:id/locations/receivable', this.getReceivableLocations.bind(this));
    this.router.post('/:id/locations', this.createLocation.bind(this));
    this.router.patch('/:id/locations/:locationId', this.updateLocation.bind(this));
    this.router.delete('/:id/locations/:locationId', this.deleteLocation.bind(this));
  }

  // ==================== Warehouses ====================

  private async findAll(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 { search, warehouseType, branchId, isActive, limit, offset } = req.query;

      const result = await this.warehousesService.findAll({
        tenantId,
        search: search as string,
        warehouseType: warehouseType as 'standard' | 'transit' | 'returns' | 'quarantine' | 'virtual',
        branchId: branchId as string,
        isActive: isActive ? isActive === '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 findOne(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 warehouse = await this.warehousesService.findOne(id, tenantId);

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

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

  private async findByCode(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 { code } = req.params;
      const warehouse = await this.warehousesService.findByCode(code, tenantId);

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

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

  private async getDefaultWarehouse(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 warehouse = await this.warehousesService.getDefaultWarehouse(tenantId);

      if (!warehouse) {
        res.status(404).json({ error: 'No default warehouse found' });
        return;
      }

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

  private async getActiveWarehouses(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 warehouses = await this.warehousesService.getActiveWarehouses(tenantId);
      res.json({ data: warehouses });
    } catch (error) {
      next(error);
    }
  }

  private async create(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: CreateWarehouseDto = req.body;
      const warehouse = await this.warehousesService.create(tenantId, dto, userId);
      res.status(201).json({ data: warehouse });
    } catch (error) {
      next(error);
    }
  }

  private async update(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 dto: UpdateWarehouseDto = req.body;
      const warehouse = await this.warehousesService.update(id, tenantId, dto, userId);

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

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

  private async delete(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 deleted = await this.warehousesService.delete(id, tenantId);

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ==================== Locations ====================

  private async getLocations(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const { search, locationType, parentId, isActive, limit, offset } = req.query;

      const result = await this.warehousesService.findAllLocations({
        warehouseId: id,
        search: search as string,
        locationType: locationType as 'zone' | 'aisle' | 'rack' | 'shelf' | 'bin',
        parentId: parentId as string,
        isActive: isActive ? isActive === '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 getLocationTree(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const locations = await this.warehousesService.getLocationTree(id);
      res.json({ data: locations });
    } catch (error) {
      next(error);
    }
  }

  private async getPickableLocations(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const locations = await this.warehousesService.getPickableLocations(id);
      res.json({ data: locations });
    } catch (error) {
      next(error);
    }
  }

  private async getReceivableLocations(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { id } = req.params;
      const locations = await this.warehousesService.getReceivableLocations(id);
      res.json({ data: locations });
    } catch (error) {
      next(error);
    }
  }

  private async createLocation(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const dto: CreateWarehouseLocationDto = { ...req.body, warehouseId: id };
      const location = await this.warehousesService.createLocation(dto);
      res.status(201).json({ data: location });
    } catch (error) {
      next(error);
    }
  }

  private async updateLocation(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { locationId } = req.params;
      const dto: UpdateWarehouseLocationDto = req.body;
      const location = await this.warehousesService.updateLocation(locationId, dto);

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

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

  private async deleteLocation(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { locationId } = req.params;
      const deleted = await this.warehousesService.deleteLocation(locationId);

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }
}
