/**
 * Quotation Controller
 * API endpoints para gestión de cotizaciones de venta
 *
 * @module Sales (MAI-010)
 * @prefix /api/v1/quotations
 */

import { Router, Request, Response, NextFunction } from 'express';
import {
  QuotationService,
  CreateQuotationDto,
  UpdateQuotationDto,
  CreateQuotationItemDto,
  UpdateQuotationItemDto,
} from '../services/quotation.service';

const router = Router();
const quotationService = new QuotationService(null as any);

/**
 * GET /api/v1/quotations
 * Lista todas las cotizaciones 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,
      salesRepId,
      status,
      dateFrom,
      dateTo,
      minTotal,
      maxTotal,
      search,
      page,
      limit,
    } = req.query;

    const result = await quotationService.findAll(
      { tenantId },
      {
        partnerId: partnerId as string,
        salesRepId: salesRepId as string,
        status: status 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/quotations/statistics
 * Estadísticas de cotizaciones
 */
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 quotationService.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/quotations/pending
 * Cotizaciones pendientes (borrador o enviadas)
 */
router.get('/pending', 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 quotationService.findPending({ tenantId });
    return res.json({ success: true, data, count: data.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/quotations/expired
 * Cotizaciones vencidas
 */
router.get('/expired', 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 quotationService.findExpired({ tenantId });
    return res.json({ success: true, data, count: data.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/quotations/by-partner/:partnerId
 * Cotizaciones 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 quotationService.findByPartner({ tenantId }, req.params.partnerId);
    return res.json({ success: true, data, count: data.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/quotations/by-number/:number
 * Buscar cotización por número
 */
router.get('/by-number/:number', 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 quotation = await quotationService.findByNumber({ tenantId }, req.params.number);
    if (!quotation) {
      return res.status(404).json({ error: 'Cotización no encontrada' });
    }

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

/**
 * GET /api/v1/quotations/:id
 * Obtiene una cotización 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 quotation = await quotationService.findById({ tenantId }, req.params.id);
    if (!quotation) {
      return res.status(404).json({ error: 'Cotización no encontrada' });
    }

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

/**
 * GET /api/v1/quotations/:id/with-items
 * Obtiene una cotización 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 quotation = await quotationService.findByIdWithItems({ tenantId }, req.params.id);
    if (!quotation) {
      return res.status(404).json({ error: 'Cotización no encontrada' });
    }

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

/**
 * POST /api/v1/quotations
 * Crea una nueva cotización
 */
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 dto: CreateQuotationDto = req.body;

    if (!dto.partnerId) {
      return res.status(400).json({ error: 'partnerId es requerido' });
    }

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

/**
 * PATCH /api/v1/quotations/:id
 * Actualiza una cotización
 */
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: UpdateQuotationDto = req.body;

    const quotation = await quotationService.update({ tenantId, userId }, req.params.id, dto);
    if (!quotation) {
      return res.status(404).json({ error: 'Cotización no encontrada' });
    }

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

/**
 * POST /api/v1/quotations/:id/items
 * Agrega una línea a la cotización
 */
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: CreateQuotationItemDto = 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 quotationService.addItem({ tenantId, userId }, req.params.id, dto);
    if (!item) {
      return res.status(404).json({ error: 'Cotización no encontrada' });
    }

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

/**
 * PATCH /api/v1/quotations/:id/items/:itemId
 * Actualiza una línea de la cotización
 */
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: UpdateQuotationItemDto = req.body;

    const item = await quotationService.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('Cannot update')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * DELETE /api/v1/quotations/:id/items/:itemId
 * Elimina una línea de la cotización
 */
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 quotationService.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('Cannot remove')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/quotations/:id/send
 * Envía la cotización 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 quotation = await quotationService.send({ tenantId, userId }, req.params.id);

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

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

/**
 * POST /api/v1/quotations/:id/accept
 * Acepta la cotización
 */
router.post('/:id/accept', 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 quotation = await quotationService.accept({ tenantId, userId }, req.params.id);

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

    return res.json({ success: true, data: quotation, message: 'Cotización aceptada' });
  } catch (error: any) {
    if (error.message?.includes('Only')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/quotations/:id/reject
 * Rechaza la cotización
 */
router.post('/:id/reject', 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 quotation = await quotationService.reject({ tenantId, userId }, req.params.id);

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

    return res.json({ success: true, data: quotation, message: 'Cotización rechazada' });
  } catch (error: any) {
    if (error.message?.includes('Only')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/quotations/:id/expire
 * Marca la cotización como vencida
 */
router.post('/:id/expire', 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 quotation = await quotationService.markExpired({ tenantId, userId }, req.params.id);

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

    return res.json({ success: true, data: quotation, message: 'Cotización marcada como vencida' });
  } catch (error: any) {
    if (error.message?.includes('Cannot expire')) {
      return res.status(400).json({ error: error.message });
    }
    return next(error);
  }
});

/**
 * POST /api/v1/quotations/:id/duplicate
 * Duplica una cotización
 */
router.post('/:id/duplicate', 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 quotation = await quotationService.duplicate({ tenantId, userId }, req.params.id);

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

    return res.status(201).json({
      success: true,
      data: quotation,
      message: 'Cotización duplicada',
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * DELETE /api/v1/quotations/:id
 * Elimina una cotización (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 quotationService.softDelete({ tenantId, userId }, req.params.id);

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

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

export default router;
