import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { warehousesService, CreateWarehouseDto, UpdateWarehouseDto, CreateLocationDto, UpdateLocationDto } from './warehouses.service.js';
import { ApiResponse, AuthenticatedRequest, ValidationError } from '../../shared/types/index.js';

// Validation schemas
const createWarehouseSchema = z.object({
  code: z.string().min(1, 'Código es requerido').max(20),
  name: z.string().min(1, 'Nombre es requerido').max(100),
  address: z.string().max(200).optional(),
  city: z.string().max(100).optional(),
  state: z.string().max(100).optional(),
  country: z.string().max(3).default('MEX'),
  postalCode: z.string().max(20).optional(),
  phone: z.string().max(30).optional(),
  email: z.string().email().max(255).optional(),
  isActive: z.boolean().default(true),
  isDefault: z.boolean().default(false),
});

const updateWarehouseSchema = z.object({
  code: z.string().min(1).max(20).optional(),
  name: z.string().min(1).max(100).optional(),
  address: z.string().max(200).optional().nullable(),
  city: z.string().max(100).optional().nullable(),
  state: z.string().max(100).optional().nullable(),
  country: z.string().max(3).optional(),
  postalCode: z.string().max(20).optional().nullable(),
  phone: z.string().max(30).optional().nullable(),
  email: z.string().email().max(255).optional().nullable(),
  isActive: z.boolean().optional(),
  isDefault: z.boolean().optional(),
});

const querySchema = z.object({
  search: z.string().optional(),
  isActive: z.coerce.boolean().optional(),
  limit: z.coerce.number().int().positive().max(100).default(50),
  offset: z.coerce.number().int().min(0).default(0),
});

const createLocationSchema = z.object({
  warehouseId: z.string().uuid('Warehouse ID inválido'),
  code: z.string().min(1, 'Código es requerido').max(30),
  name: z.string().min(1, 'Nombre es requerido').max(100),
  parentId: z.string().uuid().optional(),
  locationType: z.enum(['zone', 'aisle', 'rack', 'shelf', 'bin']).default('shelf'),
  barcode: z.string().max(50).optional(),
  isActive: z.boolean().default(true),
});

const updateLocationSchema = z.object({
  code: z.string().min(1).max(30).optional(),
  name: z.string().min(1).max(100).optional(),
  parentId: z.string().uuid().optional().nullable(),
  locationType: z.enum(['zone', 'aisle', 'rack', 'shelf', 'bin']).optional(),
  barcode: z.string().max(50).optional().nullable(),
  isActive: z.boolean().optional(),
});

const locationQuerySchema = z.object({
  warehouseId: z.string().uuid().optional(),
  parentId: z.string().uuid().optional(),
  locationType: z.enum(['zone', 'aisle', 'rack', 'shelf', 'bin']).optional(),
  isActive: z.coerce.boolean().optional(),
  limit: z.coerce.number().int().positive().max(100).default(50),
  offset: z.coerce.number().int().min(0).default(0),
});

class WarehousesControllerClass {
  // ========== WAREHOUSES ==========

  async findAll(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = querySchema.safeParse(req.query);
      if (!validation.success) {
        throw new ValidationError('Parámetros de consulta inválidos', validation.error.errors);
      }

      const result = await warehousesService.findAll({ tenantId, ...validation.data });
      res.json({ success: true, ...result } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async findById(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const warehouse = await warehousesService.findOne(req.params.id, tenantId);
      if (!warehouse) {
        return res.status(404).json({ success: false, error: 'Almacén no encontrado' } as ApiResponse);
      }

      res.json({ success: true, data: warehouse } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async findByCode(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const warehouse = await warehousesService.findByCode(req.params.code, tenantId);
      if (!warehouse) {
        return res.status(404).json({ success: false, error: 'Almacén no encontrado' } as ApiResponse);
      }

      res.json({ success: true, data: warehouse } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async getDefault(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const warehouse = await warehousesService.getDefault(tenantId);
      if (!warehouse) {
        return res.status(404).json({ success: false, error: 'No hay almacén por defecto' } as ApiResponse);
      }

      res.json({ success: true, data: warehouse } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async getActive(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const warehouses = await warehousesService.getActive(tenantId);
      res.json({ success: true, data: warehouses, total: warehouses.length } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async create(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = createWarehouseSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de almacén inválidos', validation.error.errors);
      }

      const warehouse = await warehousesService.create(tenantId, validation.data as CreateWarehouseDto, userId);
      res.status(201).json({ success: true, data: warehouse } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async update(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = updateWarehouseSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de actualización inválidos', validation.error.errors);
      }

      const warehouse = await warehousesService.update(req.params.id, tenantId, validation.data as UpdateWarehouseDto, userId);
      if (!warehouse) {
        return res.status(404).json({ success: false, error: 'Almacén no encontrado' } as ApiResponse);
      }

      res.json({ success: true, data: warehouse } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async delete(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const deleted = await warehousesService.delete(req.params.id, tenantId);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Almacén no encontrado' } as ApiResponse);
      }

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

  // ========== LOCATIONS ==========

  async findAllLocations(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = locationQuerySchema.safeParse(req.query);
      if (!validation.success) {
        throw new ValidationError('Parámetros de consulta inválidos', validation.error.errors);
      }

      const result = await warehousesService.findAllLocations({ tenantId, ...validation.data });
      res.json({ success: true, ...result } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async findLocationById(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const location = await warehousesService.findLocation(req.params.id, tenantId);
      if (!location) {
        return res.status(404).json({ success: false, error: 'Ubicación no encontrada' } as ApiResponse);
      }

      res.json({ success: true, data: location } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async getWarehouseLocations(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const locations = await warehousesService.getLocationsByWarehouse(req.params.warehouseId, tenantId);
      res.json({ success: true, data: locations, total: locations.length } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async createLocation(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = createLocationSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de ubicación inválidos', validation.error.errors);
      }

      const location = await warehousesService.createLocation(tenantId, validation.data as CreateLocationDto, userId);
      res.status(201).json({ success: true, data: location } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async updateLocation(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      const userId = req.user?.userId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const validation = updateLocationSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de actualización inválidos', validation.error.errors);
      }

      const location = await warehousesService.updateLocation(req.params.id, tenantId, validation.data as UpdateLocationDto, userId);
      if (!location) {
        return res.status(404).json({ success: false, error: 'Ubicación no encontrada' } as ApiResponse);
      }

      res.json({ success: true, data: location } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async deleteLocation(req: AuthenticatedRequest, res: Response, next: NextFunction) {
    try {
      const tenantId = req.user?.tenantId;
      if (!tenantId) {
        return res.status(400).json({ success: false, error: 'Tenant ID required' } as ApiResponse);
      }

      const deleted = await warehousesService.deleteLocation(req.params.id, tenantId);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Ubicación no encontrada' } as ApiResponse);
      }

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

export const warehousesController = new WarehousesControllerClass();
