import { Request, Response, NextFunction, Router } from 'express';
import {
  MercanciaService,
  CreateMercanciaDto,
  UpdateMercanciaDto,
} from '../services/mercancia.service';
import { MercanciaCartaPorte } from '../entities';

/**
 * Controlador para gestion de mercancias de Carta Porte
 * CFDI 3.1 Compliance - Manejo de bienes transportados
 */
export class MercanciaController {
  public router: Router;

  constructor(private readonly mercanciaService: MercanciaService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // CRUD basico
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.patch('/:id', this.update.bind(this));
    this.router.delete('/:id', this.delete.bind(this));

    // Operaciones por carta porte
    this.router.get('/carta-porte/:cartaPorteId', this.findByCartaParte.bind(this));

    // Validaciones y calculos
    this.router.get('/carta-porte/:cartaPorteId/validar-pesos', this.validarPesos.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/valor-total', this.getValorTotal.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/peligrosas', this.getMercanciasPeligrosas.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/estadisticas-peligrosas', this.getEstadisticasPeligrosas.bind(this));
  }

  /**
   * GET / - Obtiene todas las mercancias (requiere cartaPorteId en query)
   */
  private async findAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId } = req.query;

      if (!cartaPorteId) {
        res.status(400).json({ error: 'cartaPorteId es requerido en query params' });
        return;
      }

      const mercancias = await this.mercanciaService.findByCartaParte(
        tenantId,
        cartaPorteId as string
      );

      res.json({
        data: mercancias,
        total: mercancias.length,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /:id - Obtiene una mercancia por ID
   */
  private async findOne(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const mercancia = await this.mercanciaService.findById(tenantId, id);

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

      res.json({ data: mercancia });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST / - Crea una nueva mercancia
   */
  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId, ...mercanciaData } = req.body;

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

      const dto: CreateMercanciaDto = mercanciaData;
      const mercancia = await this.mercanciaService.create(tenantId, cartaPorteId, dto);

      res.status(201).json({ data: mercancia });
    } catch (error) {
      next(error);
    }
  }

  /**
   * PATCH /:id - Actualiza una mercancia existente
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const dto: UpdateMercanciaDto = req.body;
      const mercancia = await this.mercanciaService.update(tenantId, id, dto);

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

      res.json({ data: mercancia });
    } catch (error) {
      next(error);
    }
  }

  /**
   * DELETE /:id - Elimina una mercancia
   */
  private async delete(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const deleted = await this.mercanciaService.delete(tenantId, id);

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /carta-porte/:cartaPorteId - Obtiene mercancias de una Carta Porte
   */
  private async findByCartaParte(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId } = req.params;
      const mercancias = await this.mercanciaService.findByCartaParte(tenantId, cartaPorteId);

      res.json({
        data: mercancias,
        total: mercancias.length,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /carta-porte/:cartaPorteId/validar-pesos - Valida pesos de mercancias vs declarado
   */
  private async validarPesos(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId } = req.params;
      const validacion = await this.mercanciaService.validatePesosTotales(tenantId, cartaPorteId);

      res.json({ data: validacion });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /carta-porte/:cartaPorteId/valor-total - Calcula valor total de mercancias
   */
  private async getValorTotal(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId } = req.params;
      const resumen = await this.mercanciaService.calculateValorMercancia(tenantId, cartaPorteId);

      res.json({ data: resumen });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /carta-porte/:cartaPorteId/peligrosas - Obtiene solo mercancias peligrosas
   */
  private async getMercanciasPeligrosas(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId } = req.params;
      const mercancias = await this.mercanciaService.getMercanciasPeligrosas(tenantId, cartaPorteId);

      res.json({
        data: mercancias,
        total: mercancias.length,
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /carta-porte/:cartaPorteId/estadisticas-peligrosas - Estadisticas de mercancias peligrosas
   */
  private async getEstadisticasPeligrosas(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { cartaPorteId } = req.params;
      const estadisticas = await this.mercanciaService.getEstadisticasMercanciasPeligrosas(
        tenantId,
        cartaPorteId
      );

      res.json({ data: estadisticas });
    } catch (error) {
      next(error);
    }
  }
}
