/**
 * Sales Order Controller
 * API endpoints para gestión de órdenes de venta
 *
 * @module Sales (MAI-010)
 * @prefix /api/v1/sales-orders
 */

import { Router, Request, Response, NextFunction } from 'express';
import {
  SalesOrderService,
  CreateSalesOrderDto,
  UpdateSalesOrderDto,
  CreateSalesOrderItemDto,
  UpdateSalesOrderItemDto,
} from '../services/sales-order.service';

const router = Router();
const salesOrderService = new SalesOrderService(null as any);

/**
 * GET /api/v1/sales-orders
 * Lista todas las órdenes de venta del tenant
 */
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const {
      partnerId,
      userId,
      salesTeamId,
      status,
      invoiceStatus,
      deliveryStatus,
      dateFrom,
      dateTo,
      minTotal,
      maxTotal,
      search,
      page,
      limit,
    } = req.query;

    const result = await salesOrderService.findAll(
      { tenantId },
      {
        partnerId: partnerId as string,
        userId: userId as string,
        salesTeamId: salesTeamId as string,
        status: status as string,
        invoiceStatus: invoiceStatus as string,
        deliveryStatus: deliveryStatus as string,
        dateFrom: dateFrom ? new Date(dateFrom as string) : undefined,
        dateTo: dateTo ? new Date(dateTo as string) : undefined,
        minTotal: minTotal ? Number(minTotal) : undefined,
        maxTotal: maxTotal ? Number(maxTotal) : undefined,
        search: search as string,
        page: page ? Number(page) : undefined,
        limit: limit ? Number(limit) : undefined,
      },
    );

    return res.json({
      success: true,
      data: result.data,
      total: result.total,
      page: result.page,
      limit: result.limit,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/statistics
 * Estadísticas de órdenes de venta
 */
router.get('/statistics', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { dateFrom, dateTo } = req.query;
    const stats = await salesOrderService.getStatistics(
      { tenantId },
      dateFrom ? new Date(dateFrom as string) : undefined,
      dateTo ? new Date(dateTo as string) : undefined,
    );

    return res.json({ success: true, data: stats });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/pending-delivery
 * Órdenes pendientes de entrega
 */
router.get('/pending-delivery', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data = await salesOrderService.findPendingDelivery({ tenantId });
    return res.json({ success: true, data, count: data.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/pending-invoice
 * Órdenes pendientes de facturar
 */
router.get('/pending-invoice', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data = await salesOrderService.findPendingInvoice({ tenantId });
    return res.json({ success: true, data, count: data.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/by-partner/:partnerId
 * Órdenes por cliente
 */
router.get('/by-partner/:partnerId', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data = await salesOrderService.findByPartner({ tenantId }, req.params.partnerId);
    return res.json({ success: true, data, count: data.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/by-quotation/:quotationId
 * Orden generada desde una cotización
 */
router.get('/by-quotation/:quotationId', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const order = await salesOrderService.findByQuotation({ tenantId }, req.params.quotationId);
    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada para esta cotización' });
    }

    return res.json({ success: true, data: order });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/by-name/:name
 * Buscar orden por número
 */
router.get('/by-name/:name', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const order = await salesOrderService.findByName({ tenantId }, req.params.name);
    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/:id
 * Obtiene una orden por ID
 */
router.get('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const order = await salesOrderService.findById({ tenantId }, req.params.id);
    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/sales-orders/:id/with-items
 * Obtiene una orden con sus líneas
 */
router.get('/:id/with-items', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const order = await salesOrderService.findByIdWithItems({ tenantId }, req.params.id);
    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders
 * Crea una nueva orden de venta
 */
router.post('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const companyId = req.headers['x-company-id'] as string;
    const dto: CreateSalesOrderDto = req.body;

    if (!dto.partnerId || !dto.currencyId) {
      return res.status(400).json({ error: 'partnerId y currencyId son requeridos' });
    }

    const order = await salesOrderService.create({ tenantId, userId, companyId }, dto);
    return res.status(201).json({ success: true, data: order });
  } catch (error: any) {
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders/from-quotation/:quotationId
 * Crea una orden desde una cotización aceptada
 */
router.post('/from-quotation/:quotationId', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const companyId = req.headers['x-company-id'] as string;

    const order = await salesOrderService.createFromQuotation(
      { tenantId, userId, companyId },
      req.params.quotationId,
    );

    if (!order) {
      return res.status(404).json({ error: 'Cotización no encontrada' });
    }

    return res.status(201).json({
      success: true,
      data: order,
      message: 'Orden creada desde cotización',
    });
  } catch (error: any) {
    if (error.message?.includes('Only accepted')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * PATCH /api/v1/sales-orders/:id
 * Actualiza una orden de venta
 */
router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const dto: UpdateSalesOrderDto = req.body;

    const order = await salesOrderService.update({ tenantId, userId }, req.params.id, dto);
    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order });
  } catch (error: any) {
    if (error.message?.includes('Can only update')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders/:id/items
 * Agrega una línea a la orden
 */
router.post('/:id/items', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const dto: CreateSalesOrderItemDto = req.body;

    if (!dto.productName || dto.quantity === undefined || dto.unitPrice === undefined) {
      return res.status(400).json({ error: 'productName, quantity y unitPrice son requeridos' });
    }

    const item = await salesOrderService.addItem({ tenantId, userId }, req.params.id, dto);
    if (!item) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.status(201).json({ success: true, data: item });
  } catch (error: any) {
    if (error.message?.includes('Can only add')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * PATCH /api/v1/sales-orders/:id/items/:itemId
 * Actualiza una línea de la orden
 */
router.patch('/:id/items/:itemId', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const dto: UpdateSalesOrderItemDto = req.body;

    const item = await salesOrderService.updateItem(
      { tenantId, userId },
      req.params.id,
      req.params.itemId,
      dto,
    );

    if (!item) {
      return res.status(404).json({ error: 'Línea no encontrada' });
    }

    return res.json({ success: true, data: item });
  } catch (error: any) {
    if (error.message?.includes('Can only update')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * DELETE /api/v1/sales-orders/:id/items/:itemId
 * Elimina una línea de la orden
 */
router.delete('/:id/items/:itemId', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const deleted = await salesOrderService.removeItem(
      { tenantId, userId },
      req.params.id,
      req.params.itemId,
    );

    if (!deleted) {
      return res.status(404).json({ error: 'Línea no encontrada' });
    }

    return res.json({ success: true, message: 'Línea eliminada' });
  } catch (error: any) {
    if (error.message?.includes('Can only remove')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders/:id/send
 * Envía la orden al cliente
 */
router.post('/:id/send', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const order = await salesOrderService.send({ tenantId, userId }, req.params.id);

    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order, message: 'Orden enviada' });
  } catch (error: any) {
    if (error.message?.includes('Only draft')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders/:id/confirm
 * Confirma la orden de venta
 */
router.post('/:id/confirm', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const order = await salesOrderService.confirm({ tenantId, userId }, req.params.id);

    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order, message: 'Orden confirmada' });
  } catch (error: any) {
    if (error.message?.includes('Can only confirm')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders/:id/cancel
 * Cancela la orden de venta
 */
router.post('/:id/cancel', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const order = await salesOrderService.cancel({ tenantId, userId }, req.params.id);

    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order, message: 'Orden cancelada' });
  } catch (error: any) {
    if (error.message?.includes('Cannot cancel')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/sales-orders/:id/done
 * Marca la orden como completada
 */
router.post('/:id/done', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const order = await salesOrderService.markDone({ tenantId, userId }, req.params.id);

    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order, message: 'Orden completada' });
  } catch (error: any) {
    if (error.message?.includes('Only confirmed')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * PATCH /api/v1/sales-orders/:id/items/:itemId/delivery
 * Actualiza la cantidad entregada de una línea
 */
router.patch('/:id/items/:itemId/delivery', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const { quantityDelivered } = req.body;

    if (quantityDelivered === undefined) {
      return res.status(400).json({ error: 'quantityDelivered es requerido' });
    }

    const item = await salesOrderService.updateItemDelivery(
      { tenantId, userId },
      req.params.id,
      req.params.itemId,
      Number(quantityDelivered),
    );

    if (!item) {
      return res.status(404).json({ error: 'Línea no encontrada' });
    }

    return res.json({ success: true, data: item, message: 'Entrega actualizada' });
  } catch (error: any) {
    if (error.message?.includes('cannot exceed')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * PATCH /api/v1/sales-orders/:id/invoice-status
 * Actualiza el estado de facturación
 */
router.patch('/:id/invoice-status', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const { status } = req.body;

    if (!status || !['pending', 'partial', 'invoiced'].includes(status)) {
      return res.status(400).json({ error: 'status debe ser pending, partial o invoiced' });
    }

    const order = await salesOrderService.updateInvoiceStatus(
      { tenantId, userId },
      req.params.id,
      status,
    );

    if (!order) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, data: order, message: 'Estado de facturación actualizado' });
  } catch (error) {
    return next(error);
  }
});

/**
 * DELETE /api/v1/sales-orders/:id
 * Elimina una orden (soft delete)
 */
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const userId = (req as any).user?.id;
    const deleted = await salesOrderService.softDelete({ tenantId, userId }, req.params.id);

    if (!deleted) {
      return res.status(404).json({ error: 'Orden no encontrada' });
    }

    return res.json({ success: true, message: 'Orden eliminada' });
  } catch (error: any) {
    if (error.message?.includes('Cannot delete')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

export default router;
