import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { productsService, CreateProductDto, UpdateProductDto, CreateCategoryDto, UpdateCategoryDto } from './products.service.js';
import { ApiResponse, AuthenticatedRequest, ValidationError } from '../../shared/types/index.js';

// Validation schemas
const createProductSchema = z.object({
  sku: z.string().min(1, 'SKU es requerido').max(50),
  name: z.string().min(1, 'Nombre es requerido').max(200),
  description: z.string().optional(),
  shortName: z.string().max(50).optional(),
  barcode: z.string().max(50).optional(),
  categoryId: z.string().uuid().optional(),
  productType: z.enum(['product', 'service', 'consumable', 'kit']).default('product'),
  salePrice: z.coerce.number().min(0).optional(),
  costPrice: z.coerce.number().min(0).optional(),
  currency: z.string().length(3).default('MXN'),
  taxRate: z.coerce.number().min(0).max(100).optional(),
  isActive: z.boolean().default(true),
  isSellable: z.boolean().default(true),
  isPurchasable: z.boolean().default(true),
});

const updateProductSchema = z.object({
  sku: z.string().min(1).max(50).optional(),
  name: z.string().min(1).max(200).optional(),
  description: z.string().optional().nullable(),
  shortName: z.string().max(50).optional().nullable(),
  barcode: z.string().max(50).optional().nullable(),
  categoryId: z.string().uuid().optional().nullable(),
  productType: z.enum(['product', 'service', 'consumable', 'kit']).optional(),
  salePrice: z.coerce.number().min(0).optional(),
  costPrice: z.coerce.number().min(0).optional(),
  currency: z.string().length(3).optional(),
  taxRate: z.coerce.number().min(0).max(100).optional(),
  isActive: z.boolean().optional(),
  isSellable: z.boolean().optional(),
  isPurchasable: z.boolean().optional(),
});

const querySchema = z.object({
  search: z.string().optional(),
  categoryId: z.string().uuid().optional(),
  productType: z.enum(['product', 'service', 'consumable', 'kit']).optional(),
  isActive: z.coerce.boolean().optional(),
  isSellable: z.coerce.boolean().optional(),
  isPurchasable: z.coerce.boolean().optional(),
  limit: z.coerce.number().int().positive().max(100).default(50),
  offset: z.coerce.number().int().min(0).default(0),
});

const createCategorySchema = z.object({
  code: z.string().min(1, 'Código es requerido').max(30),
  name: z.string().min(1, 'Nombre es requerido').max(100),
  description: z.string().optional(),
  parentId: z.string().uuid().optional(),
  isActive: z.boolean().default(true),
});

const updateCategorySchema = z.object({
  code: z.string().min(1).max(30).optional(),
  name: z.string().min(1).max(100).optional(),
  description: z.string().optional().nullable(),
  parentId: z.string().uuid().optional().nullable(),
  isActive: z.boolean().optional(),
});

const categoryQuerySchema = z.object({
  search: z.string().optional(),
  parentId: z.string().uuid().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 ProductsControllerClass {
  // ========== PRODUCTS ==========

  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 productsService.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 product = await productsService.findOne(req.params.id, tenantId);
      if (!product) {
        return res.status(404).json({ success: false, error: 'Producto no encontrado' } as ApiResponse);
      }

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

  async findBySku(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 product = await productsService.findBySku(req.params.sku, tenantId);
      if (!product) {
        return res.status(404).json({ success: false, error: 'Producto no encontrado' } as ApiResponse);
      }

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

  async findByBarcode(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 product = await productsService.findByBarcode(req.params.barcode, tenantId);
      if (!product) {
        return res.status(404).json({ success: false, error: 'Producto no encontrado' } as ApiResponse);
      }

      res.json({ success: true, data: product } 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 = createProductSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de producto inválidos', validation.error.errors);
      }

      const product = await productsService.create(tenantId, validation.data as CreateProductDto, userId);
      res.status(201).json({ success: true, data: product } 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 = updateProductSchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de actualización inválidos', validation.error.errors);
      }

      const product = await productsService.update(req.params.id, tenantId, validation.data as UpdateProductDto, userId);
      if (!product) {
        return res.status(404).json({ success: false, error: 'Producto no encontrado' } as ApiResponse);
      }

      res.json({ success: true, data: product } 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 productsService.delete(req.params.id, tenantId);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Producto no encontrado' } as ApiResponse);
      }

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

  async getSellable(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 { limit, offset } = querySchema.parse(req.query);
      const result = await productsService.getSellableProducts(tenantId, limit, offset);
      res.json({ success: true, ...result } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async getPurchasable(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 { limit, offset } = querySchema.parse(req.query);
      const result = await productsService.getPurchasableProducts(tenantId, limit, offset);
      res.json({ success: true, ...result } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  // ========== CATEGORIES ==========

  async findAllCategories(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 = categoryQuerySchema.safeParse(req.query);
      if (!validation.success) {
        throw new ValidationError('Parámetros de consulta inválidos', validation.error.errors);
      }

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

  async findCategoryById(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 category = await productsService.findCategory(req.params.id, tenantId);
      if (!category) {
        return res.status(404).json({ success: false, error: 'Categoría no encontrada' } as ApiResponse);
      }

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

  async createCategory(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 = createCategorySchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de categoría inválidos', validation.error.errors);
      }

      const category = await productsService.createCategory(tenantId, validation.data as CreateCategoryDto, userId);
      res.status(201).json({ success: true, data: category } as ApiResponse);
    } catch (error) {
      next(error);
    }
  }

  async updateCategory(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 = updateCategorySchema.safeParse(req.body);
      if (!validation.success) {
        throw new ValidationError('Datos de actualización inválidos', validation.error.errors);
      }

      const category = await productsService.updateCategory(req.params.id, tenantId, validation.data as UpdateCategoryDto, userId);
      if (!category) {
        return res.status(404).json({ success: false, error: 'Categoría no encontrada' } as ApiResponse);
      }

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

  async deleteCategory(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 productsService.deleteCategory(req.params.id, tenantId);
      if (!deleted) {
        return res.status(404).json({ success: false, error: 'Categoría no encontrada' } as ApiResponse);
      }

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

export const productsController = new ProductsControllerClass();
