/**
 * InvoiceController - REST Controller for Invoices
 *
 * Endpoints for invoice management including CRUD and workflow operations.
 *
 * @module Invoices
 */

import { Router, Request, Response } from 'express';
import { DataSource } from 'typeorm';
import { InvoiceService, InvoiceFilters } from '../services';

export function createInvoiceController(dataSource: DataSource): Router {
  const router = Router();
  const service = new InvoiceService(dataSource);

  // ==================== CRUD ENDPOINTS ====================

  /**
   * GET /
   * List invoices with filters and pagination
   */
  router.get('/', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const filters: InvoiceFilters = {
        invoiceType: req.query.invoiceType as any,
        invoiceContext: req.query.invoiceContext as any,
        status: req.query.status as any,
        partnerId: req.query.partnerId as string,
        startDate: req.query.startDate ? new Date(req.query.startDate as string) : undefined,
        endDate: req.query.endDate ? new Date(req.query.endDate as string) : undefined,
        overdue: req.query.overdue === 'true',
        search: req.query.search as string,
      };

      const page = parseInt(req.query.page as string) || 1;
      const limit = parseInt(req.query.limit as string) || 20;

      const result = await service.findAll(ctx, filters, page, limit);
      res.json(result);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /dashboard
   * Get dashboard statistics
   */
  router.get('/dashboard', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const stats = await service.getDashboardStats(ctx);
      res.json(stats);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /summary
   * Get invoice summary
   */
  router.get('/summary', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const filters: InvoiceFilters = {
        invoiceContext: req.query.invoiceContext as any,
        startDate: req.query.startDate ? new Date(req.query.startDate as string) : undefined,
        endDate: req.query.endDate ? new Date(req.query.endDate as string) : undefined,
      };

      const summary = await service.getSummary(ctx, filters);
      res.json(summary);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /by-number/:invoiceNumber
   * Get invoice by number
   */
  router.get('/by-number/:invoiceNumber', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.findByNumber(ctx, req.params.invoiceNumber);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /:id
   * Get invoice by ID
   */
  router.get('/:id', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.findWithItems(ctx, req.params.id);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /
   * Create new invoice
   */
  router.post('/', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.create(ctx, req.body);
      res.status(201).json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * PUT /:id
   * Update invoice
   */
  router.put('/:id', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.update(ctx, req.params.id, req.body);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * DELETE /:id
   * Delete invoice (soft delete)
   */
  router.delete('/:id', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const deleted = await service.softDelete(ctx, req.params.id);
      if (!deleted) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  // ==================== ITEM ENDPOINTS ====================

  /**
   * POST /:id/items
   * Add items to invoice
   */
  router.post('/:id/items', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const items = await service.addItems(ctx, req.params.id, req.body.items || [req.body]);
      res.status(201).json(items);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * PUT /items/:itemId
   * Update invoice item
   */
  router.put('/items/:itemId', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const item = await service.updateItem(ctx, req.params.itemId, req.body);
      if (!item) {
        res.status(404).json({ error: 'Item no encontrado' });
        return;
      }

      res.json(item);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * DELETE /items/:itemId
   * Remove invoice item
   */
  router.delete('/items/:itemId', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const deleted = await service.removeItem(ctx, req.params.itemId);
      if (!deleted) {
        res.status(404).json({ error: 'Item no encontrado' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  // ==================== WORKFLOW ENDPOINTS ====================

  /**
   * POST /:id/validate
   * Validate invoice (draft -> validated)
   */
  router.post('/:id/validate', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.validate(ctx, req.params.id);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /:id/send
   * Send invoice (validated -> sent)
   */
  router.post('/:id/send', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.send(ctx, req.params.id);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /:id/record-payment
   * Record partial payment
   */
  router.post('/:id/record-payment', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const { amount } = req.body;
      if (!amount || amount <= 0) {
        res.status(400).json({ error: 'Monto de pago invalido' });
        return;
      }

      const invoice = await service.recordPartialPayment(ctx, req.params.id, amount);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /:id/mark-paid
   * Mark invoice as paid
   */
  router.post('/:id/mark-paid', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.markAsPaid(ctx, req.params.id, req.body.paymentReference);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /:id/void
   * Void invoice
   */
  router.post('/:id/void', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.void(ctx, req.params.id, req.body.reason);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /:id/cancel
   * Cancel invoice
   */
  router.post('/:id/cancel', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.cancel(ctx, req.params.id, req.body.reason);
      if (!invoice) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /:id/refund
   * Create refund (credit note)
   */
  router.post('/:id/refund', async (req: Request, res: Response): Promise<void> => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const creditNote = await service.createRefund(ctx, req.params.id, req.body.reason);
      if (!creditNote) {
        res.status(404).json({ error: 'Factura no encontrada' });
        return;
      }

      res.status(201).json(creditNote);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  // ==================== GENERATION ENDPOINTS ====================

  /**
   * POST /generate/from-sales-order
   * Generate invoice from sales order
   */
  router.post('/generate/from-sales-order', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const { salesOrderId, ...salesOrderData } = req.body;
      const invoice = await service.generateFromSalesOrder(ctx, salesOrderId, salesOrderData);
      res.status(201).json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /generate/for-subscription
   * Generate invoice for subscription period
   */
  router.post('/generate/for-subscription', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const invoice = await service.generateForSubscription(ctx, req.body);
      res.status(201).json(invoice);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  // ==================== MAINTENANCE ENDPOINTS ====================

  /**
   * POST /update-overdue
   * Update overdue status for all invoices
   */
  router.post('/update-overdue', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const count = await service.updateOverdueStatus(ctx);
      res.json({ updated: count });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  return router;
}
