/**
 * AccountingController - Controlador de Contabilidad
 *
 * Endpoints para catálogo de cuentas y pólizas.
 *
 * @module Finance
 */

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

export function createAccountingController(dataSource: DataSource): Router {
  const router = Router();
  const service = new AccountingService(dataSource);

  // ==================== CATÁLOGO DE CUENTAS ====================

  /**
   * GET /accounts
   * Lista cuentas contables con filtros y paginación
   */
  router.get('/accounts', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const options = {
        page: parseInt(req.query.page as string) || 1,
        limit: parseInt(req.query.limit as string) || 50,
        accountType: req.query.accountType as any,
        status: req.query.status as any,
        parentId: req.query.parentId as string,
        search: req.query.search as string,
        acceptsMovements: req.query.acceptsMovements === 'true' ? true : req.query.acceptsMovements === 'false' ? false : undefined,
      };

      const result = await service.findAllAccounts(ctx, options);
      res.json(result);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /accounts/tree
   * Obtiene árbol jerárquico de cuentas
   */
  router.get('/accounts/tree', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

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

  /**
   * GET /accounts/:id
   * Obtiene una cuenta por ID
   */
  router.get('/accounts/: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 account = await service.findAccountById(ctx, req.params.id);
      if (!account) {
        res.status(404).json({ error: 'Cuenta no encontrada' });
        return;
      }

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

  /**
   * GET /accounts/code/:code
   * Obtiene una cuenta por código
   */
  router.get('/accounts/code/:code', 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 account = await service.findAccountByCode(ctx, req.params.code);
      if (!account) {
        res.status(404).json({ error: 'Cuenta no encontrada' });
        return;
      }

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

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

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

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

      const account = await service.updateAccount(ctx, req.params.id, req.body);
      res.json(account);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

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

      await service.deleteAccount(ctx, req.params.id);
      res.status(204).send();
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  // ==================== PÓLIZAS CONTABLES ====================

  /**
   * GET /entries
   * Lista pólizas con filtros y paginación
   */
  router.get('/entries', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const options = {
        page: parseInt(req.query.page as string) || 1,
        limit: parseInt(req.query.limit as string) || 20,
        entryType: req.query.entryType as any,
        status: req.query.status 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,
        projectId: req.query.projectId as string,
        fiscalYear: req.query.fiscalYear ? parseInt(req.query.fiscalYear as string) : undefined,
        fiscalPeriod: req.query.fiscalPeriod ? parseInt(req.query.fiscalPeriod as string) : undefined,
        search: req.query.search as string,
      };

      const result = await service.findAllEntries(ctx, options);
      res.json(result);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /entries/:id
   * Obtiene una póliza por ID
   */
  router.get('/entries/: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 entry = await service.findEntryById(ctx, req.params.id);
      if (!entry) {
        res.status(404).json({ error: 'Póliza no encontrada' });
        return;
      }

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

  /**
   * POST /entries
   * Crea una nueva póliza
   */
  router.post('/entries', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

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

  /**
   * POST /entries/:id/submit
   * Envía póliza a aprobación
   */
  router.post('/entries/:id/submit', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

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

  /**
   * POST /entries/:id/approve
   * Aprueba una póliza
   */
  router.post('/entries/:id/approve', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

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

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

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

  /**
   * POST /entries/:id/cancel
   * Cancela una póliza
   */
  router.post('/entries/: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 { reason } = req.body;
      if (!reason) {
        res.status(400).json({ error: 'Se requiere motivo de cancelación' });
        return;
      }

      const entry = await service.cancelEntry(ctx, req.params.id, reason);
      res.json(entry);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  /**
   * POST /entries/:id/reverse
   * Reversa una póliza
   */
  router.post('/entries/:id/reverse', 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 { reason } = req.body;
      if (!reason) {
        res.status(400).json({ error: 'Se requiere motivo de reverso' });
        return;
      }

      const entry = await service.reverseEntry(ctx, req.params.id, reason);
      res.json(entry);
    } catch (error) {
      res.status(400).json({ error: (error as Error).message });
    }
  });

  // ==================== REPORTES ====================

  /**
   * GET /reports/trial-balance
   * Obtiene balanza de comprobación
   */
  router.get('/reports/trial-balance', async (req: Request, res: Response) => {
    try {
      const ctx = {
        tenantId: req.headers['x-tenant-id'] as string,
        userId: (req as any).user?.id,
      };

      const fiscalYear = parseInt(req.query.fiscalYear as string) || new Date().getFullYear();
      const fiscalPeriod = req.query.fiscalPeriod ? parseInt(req.query.fiscalPeriod as string) : undefined;

      const result = await service.getTrialBalance(ctx, fiscalYear, fiscalPeriod);
      res.json(result);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  /**
   * GET /reports/account-ledger/:accountId
   * Obtiene mayor de una cuenta
   */
  router.get('/reports/account-ledger/:accountId', 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 startDate = new Date(req.query.startDate as string);
      const endDate = new Date(req.query.endDate as string);

      if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
        res.status(400).json({ error: 'Fechas inválidas' });
        return;
      }

      const result = await service.getAccountLedger(ctx, req.params.accountId, startDate, endDate);
      res.json(result);
    } catch (error) {
      res.status(500).json({ error: (error as Error).message });
    }
  });

  return router;
}
