/**
 * PaymentController - REST Controller for Payments
 *
 * Endpoints for payment management including CRUD, allocation, and workflow operations.
 *
 * @module Invoices
 */

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

export function createPaymentController(dataSource: DataSource): Router {
  const router = Router();
  const service = new PaymentService(dataSource);

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

  /**
   * GET /
   * List payments 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: PaymentFilters = {
        paymentType: req.query.paymentType as any,
        partnerId: req.query.partnerId as string,
        status: req.query.status as any,
        paymentMethod: req.query.paymentMethod 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,
        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 /summary
   * Get payment 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: PaymentFilters = {
        paymentType: req.query.paymentType 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-partner/:partnerId
   * Get payments by partner
   */
  router.get('/by-partner/:partnerId', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

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

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

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

      const invoices = await service.getInvoicesForAllocation(ctx, req.params.partnerId);
      res.json(invoices);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /:id
   * Get payment 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 payment = await service.findWithAllocations(ctx, req.params.id);
      if (!payment) {
        res.status(404).json({ error: 'Pago no encontrado' });
        return;
      }

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

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

      const unallocatedAmount = await service.getUnallocatedAmount(ctx, req.params.id);
      res.json({ unallocatedAmount });
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

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

      const allocations = await service.getAllocations(ctx, req.params.id);
      res.json(allocations);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /
   * Create new payment
   */
  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 payment = await service.create(ctx, req.body);
      res.status(201).json(payment);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * PUT /:id
   * Update payment
   */
  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 payment = await service.update(ctx, req.params.id, req.body);
      if (!payment) {
        res.status(404).json({ error: 'Pago no encontrado' });
        return;
      }

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

  /**
   * DELETE /:id
   * Delete payment (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: 'Pago no encontrado' });
        return;
      }

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

  // ==================== ALLOCATION ENDPOINTS ====================

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

      const { allocations } = req.body;
      if (!allocations || !Array.isArray(allocations) || allocations.length === 0) {
        res.status(400).json({ error: 'Se requieren asignaciones' });
        return;
      }

      const result = await service.allocateToInvoices(ctx, req.params.id, allocations);
      res.status(201).json(result);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * DELETE /allocations/:allocationId
   * Remove allocation
   */
  router.delete('/allocations/:allocationId', 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.removeAllocation(ctx, req.params.allocationId);
      if (!deleted) {
        res.status(404).json({ error: 'Asignacion no encontrada' });
        return;
      }

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

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

  /**
   * POST /:id/confirm
   * Confirm payment (draft -> confirmed)
   */
  router.post('/:id/confirm', 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 payment = await service.confirm(ctx, req.params.id);
      if (!payment) {
        res.status(404).json({ error: 'Pago no encontrado' });
        return;
      }

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

  /**
   * POST /:id/reconcile
   * Reconcile payment (confirmed -> reconciled)
   */
  router.post('/:id/reconcile', 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 payment = await service.reconcile(ctx, req.params.id);
      if (!payment) {
        res.status(404).json({ error: 'Pago no encontrado' });
        return;
      }

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

  /**
   * POST /:id/cancel
   * Cancel payment
   */
  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 payment = await service.cancel(ctx, req.params.id, req.body.reason);
      if (!payment) {
        res.status(404).json({ error: 'Pago no encontrado' });
        return;
      }

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

  return router;
}
