import { Request, Response, NextFunction, Router } from 'express';
import {
  FiguraTransporteService,
  CreateFiguraDto,
  UpdateFiguraDto,
} from '../services/figura-transporte.service';
import { TipoFigura } from '../entities';

/**
 * Controlador para gestion de figuras de transporte de Carta Porte
 * CFDI 3.1 Compliance - Manejo de operadores, propietarios y arrendadores
 */
export class FiguraTransporteController {
  public router: Router;

  constructor(private readonly figuraService: FiguraTransporteService) {
    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));
    this.router.get('/carta-porte/:cartaPorteId/validar', this.validarFigurasRequeridas.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/resumen', this.getResumenPorTipo.bind(this));

    // Filtros por tipo
    this.router.get('/tipo/:tipo', this.findByTipo.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/operadores', this.getOperadores.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/propietarios', this.getPropietarios.bind(this));
    this.router.get('/carta-porte/:cartaPorteId/arrendadores', this.getArrendadores.bind(this));

    // Operaciones de alta rapida
    this.router.post('/carta-porte/:cartaPorteId/operador', this.addOperador.bind(this));
    this.router.post('/carta-porte/:cartaPorteId/propietario', this.addPropietario.bind(this));
    this.router.post('/carta-porte/:cartaPorteId/arrendador', this.addArrendador.bind(this));
  }

  /**
   * GET / - Obtiene todas las figuras (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 figuras = await this.figuraService.findByCartaParte(
        tenantId,
        cartaPorteId as string
      );

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

  /**
   * GET /:id - Obtiene una figura 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 figura = await this.figuraService.findById(tenantId, id);

      if (!figura) {
        res.status(404).json({ error: 'Figura de transporte no encontrada' });
        return;
      }

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

  /**
   * POST / - Crea una nueva figura de transporte
   */
  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, ...figuraData } = req.body;

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

      const dto: CreateFiguraDto = figuraData;
      const figura = await this.figuraService.create(tenantId, cartaPorteId, dto);

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

  /**
   * PATCH /:id - Actualiza una figura 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: UpdateFiguraDto = req.body;
      const figura = await this.figuraService.update(tenantId, id, dto);

      if (!figura) {
        res.status(404).json({ error: 'Figura de transporte no encontrada' });
        return;
      }

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

  /**
   * DELETE /:id - Elimina una figura de transporte
   */
  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.figuraService.delete(tenantId, id);

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

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

  /**
   * GET /carta-porte/:cartaPorteId - Obtiene figuras 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 figuras = await this.figuraService.findByCartaParte(tenantId, cartaPorteId);

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

  /**
   * GET /tipo/:tipo - Filtra figuras por tipo (requiere cartaPorteId en query)
   */
  private async findByTipo(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 { tipo } = req.params;
      const { cartaPorteId } = req.query;

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

      // Validar tipo
      const tiposValidos = Object.values(TipoFigura);
      if (!tiposValidos.includes(tipo as TipoFigura)) {
        res.status(400).json({
          error: `tipo debe ser uno de: ${tiposValidos.join(', ')}`,
        });
        return;
      }

      // Obtener todas las figuras y filtrar por tipo
      const todasFiguras = await this.figuraService.findByCartaParte(
        tenantId,
        cartaPorteId as string
      );

      const figurasFiltradas = todasFiguras.filter(f => f.tipoFigura === tipo);

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

  /**
   * GET /carta-porte/:cartaPorteId/validar - Valida figuras requeridas
   */
  private async validarFigurasRequeridas(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.figuraService.validateFigurasRequeridas(tenantId, cartaPorteId);

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

  /**
   * GET /carta-porte/:cartaPorteId/resumen - Obtiene resumen por tipo de figura
   */
  private async getResumenPorTipo(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.figuraService.getResumenPorTipo(tenantId, cartaPorteId);

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

  /**
   * GET /carta-porte/:cartaPorteId/operadores - Obtiene solo operadores
   */
  private async getOperadores(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 operadores = await this.figuraService.getOperadores(tenantId, cartaPorteId);

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

  /**
   * GET /carta-porte/:cartaPorteId/propietarios - Obtiene solo propietarios
   */
  private async getPropietarios(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 propietarios = await this.figuraService.getPropietarios(tenantId, cartaPorteId);

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

  /**
   * GET /carta-porte/:cartaPorteId/arrendadores - Obtiene solo arrendadores
   */
  private async getArrendadores(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 arrendadores = await this.figuraService.getArrendadores(tenantId, cartaPorteId);

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

  /**
   * POST /carta-porte/:cartaPorteId/operador - Agrega un operador con datos minimos
   */
  private async addOperador(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 { rfcFigura, nombreFigura, numLicencia, pais, estado, codigoPostal } = req.body;

      if (!rfcFigura || !nombreFigura || !numLicencia) {
        res.status(400).json({
          error: 'rfcFigura, nombreFigura y numLicencia son requeridos para operador',
        });
        return;
      }

      const figura = await this.figuraService.addOperador(tenantId, cartaPorteId, {
        rfcFigura,
        nombreFigura,
        numLicencia,
        pais,
        estado,
        codigoPostal,
      });

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

  /**
   * POST /carta-porte/:cartaPorteId/propietario - Agrega un propietario de mercancia
   */
  private async addPropietario(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 { rfcFigura, nombreFigura, pais, estado, codigoPostal, calle, partesTransporte } = req.body;

      if (!rfcFigura || !nombreFigura) {
        res.status(400).json({
          error: 'rfcFigura y nombreFigura son requeridos para propietario',
        });
        return;
      }

      const figura = await this.figuraService.addPropietario(tenantId, cartaPorteId, {
        rfcFigura,
        nombreFigura,
        pais,
        estado,
        codigoPostal,
        calle,
        partesTransporte,
      });

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

  /**
   * POST /carta-porte/:cartaPorteId/arrendador - Agrega un arrendador
   */
  private async addArrendador(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 { rfcFigura, nombreFigura, pais, estado, codigoPostal, calle, partesTransporte } = req.body;

      if (!rfcFigura || !nombreFigura) {
        res.status(400).json({
          error: 'rfcFigura y nombreFigura son requeridos para arrendador',
        });
        return;
      }

      const figura = await this.figuraService.addArrendador(tenantId, cartaPorteId, {
        rfcFigura,
        nombreFigura,
        pais,
        estado,
        codigoPostal,
        calle,
        partesTransporte,
      });

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