import { Request, Response, NextFunction, Router } from 'express';
import {
  DocumentoFlotaService,
  CreateDocumentoFlotaDto,
  UpdateDocumentoFlotaDto,
} from '../services/documento-flota.service';
import { TipoDocumento, TipoEntidadDocumento } from '../entities';

/**
 * Controller for managing fleet documents (documentos de flota)
 * Handles documents for units, trailers, and operators
 */
export class DocumentoFlotaController {
  public router: Router;

  constructor(private readonly documentoFlotaService: DocumentoFlotaService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // CRUD básico
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/por-vencer', this.getDocumentosPorVencer.bind(this));
    this.router.get('/vencidos', this.getDocumentosVencidos.bind(this));
    this.router.get('/resumen-vencimientos', this.getResumenVencimientos.bind(this));
    this.router.get('/entidades-bloqueadas', this.getEntidadesBloqueadas.bind(this));
    this.router.get('/alertas', this.getAlertasVencimiento.bind(this));
    this.router.get('/unidad/:unidadId', this.findByUnidad.bind(this));
    this.router.get('/operador/:operadorId', this.findByOperador.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 específicas
    this.router.post('/:id/renovar', this.renovar.bind(this));
  }

  /**
   * GET /documentos-flota
   * Find all documents with optional filters
   * Query params: unidadId, operadorId, tipoDocumento, estadoVigencia
   */
  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 { unidadId, operadorId, tipoDocumento, estadoVigencia } = req.query;

      // Handle different filter scenarios
      let documentos;

      if (unidadId) {
        documentos = await this.documentoFlotaService.findByUnidad(tenantId, unidadId as string);
      } else if (operadorId) {
        documentos = await this.documentoFlotaService.findByOperador(tenantId, operadorId as string);
      } else if (tipoDocumento) {
        documentos = await this.documentoFlotaService.findByTipo(tenantId, tipoDocumento as TipoDocumento);
      } else if (estadoVigencia === 'vencido') {
        documentos = await this.documentoFlotaService.findVencidos(tenantId);
      } else if (estadoVigencia === 'por_vencer') {
        documentos = await this.documentoFlotaService.findPorVencer(tenantId, 30);
      } else {
        // Default: return documents expiring soon (all types)
        documentos = await this.documentoFlotaService.findPorVencer(tenantId, 365);
      }

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

  /**
   * GET /documentos-flota/:id
   * Find a document by 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 documento = await this.documentoFlotaService.findById(tenantId, id);

      if (!documento) {
        res.status(404).json({ error: 'Documento no encontrado' });
        return;
      }

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

  /**
   * POST /documentos-flota
   * Create a new fleet document
   */
  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const dto: CreateDocumentoFlotaDto = req.body;

      // Validate required fields
      if (!dto.entidadTipo) {
        res.status(400).json({ error: 'Tipo de entidad es requerido' });
        return;
      }
      if (!dto.entidadId) {
        res.status(400).json({ error: 'ID de entidad es requerido' });
        return;
      }
      if (!dto.tipoDocumento) {
        res.status(400).json({ error: 'Tipo de documento es requerido' });
        return;
      }
      if (!dto.nombre) {
        res.status(400).json({ error: 'Nombre del documento es requerido' });
        return;
      }

      const documento = await this.documentoFlotaService.create(tenantId, dto, userId);
      res.status(201).json({ data: documento });
    } catch (error) {
      next(error);
    }
  }

  /**
   * PATCH /documentos-flota/:id
   * Update a fleet document
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const dto: UpdateDocumentoFlotaDto = req.body;

      const documento = await this.documentoFlotaService.update(tenantId, id, dto, userId);
      res.json({ data: documento });
    } catch (error) {
      if (error instanceof Error && error.message.includes('no encontrado')) {
        res.status(404).json({ error: error.message });
        return;
      }
      next(error);
    }
  }

  /**
   * DELETE /documentos-flota/:id
   * Soft delete a fleet document
   */
  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;

      await this.documentoFlotaService.delete(tenantId, id);
      res.status(204).send();
    } catch (error) {
      if (error instanceof Error && error.message.includes('no encontrado')) {
        res.status(404).json({ error: error.message });
        return;
      }
      next(error);
    }
  }

  /**
   * GET /documentos-flota/unidad/:unidadId
   * Get all documents for a specific unit
   */
  private async findByUnidad(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 { unidadId } = req.params;
      const documentos = await this.documentoFlotaService.findByUnidad(tenantId, unidadId);
      res.json({ data: documentos, total: documentos.length });
    } catch (error) {
      if (error instanceof Error && error.message.includes('no encontrada')) {
        res.status(404).json({ error: error.message });
        return;
      }
      next(error);
    }
  }

  /**
   * GET /documentos-flota/operador/:operadorId
   * Get all documents for a specific operator
   */
  private async findByOperador(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 { operadorId } = req.params;
      const documentos = await this.documentoFlotaService.findByOperador(tenantId, operadorId);
      res.json({ data: documentos, total: documentos.length });
    } catch (error) {
      if (error instanceof Error && error.message.includes('no encontrado')) {
        res.status(404).json({ error: error.message });
        return;
      }
      next(error);
    }
  }

  /**
   * GET /documentos-flota/por-vencer
   * Get documents expiring soon
   * Query param: dias (default: 30)
   */
  private async getDocumentosPorVencer(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 { dias } = req.query;
      const diasAntelacion = dias ? parseInt(dias as string, 10) : 30;

      if (isNaN(diasAntelacion) || diasAntelacion < 1) {
        res.status(400).json({ error: 'El parámetro días debe ser un número positivo' });
        return;
      }

      const documentos = await this.documentoFlotaService.findPorVencer(tenantId, diasAntelacion);
      res.json({ data: documentos, total: documentos.length, diasConsultados: diasAntelacion });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /documentos-flota/vencidos
   * Get all expired documents
   */
  private async getDocumentosVencidos(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 documentos = await this.documentoFlotaService.findVencidos(tenantId);
      res.json({ data: documentos, total: documentos.length });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /documentos-flota/:id/renovar
   * Renew a document with a new expiration date
   * Body: { nuevaFechaVencimiento: Date }
   */
  private async renovar(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const { nuevaFechaVencimiento } = req.body;

      if (!nuevaFechaVencimiento) {
        res.status(400).json({ error: 'Nueva fecha de vencimiento es requerida' });
        return;
      }

      const fechaVencimiento = new Date(nuevaFechaVencimiento);
      if (isNaN(fechaVencimiento.getTime())) {
        res.status(400).json({ error: 'Fecha de vencimiento inválida' });
        return;
      }

      const documento = await this.documentoFlotaService.renovar(tenantId, id, fechaVencimiento, userId);
      res.json({ data: documento, message: 'Documento renovado exitosamente' });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message.includes('no encontrado')) {
          res.status(404).json({ error: error.message });
          return;
        }
        if (error.message.includes('fecha futura')) {
          res.status(400).json({ error: error.message });
          return;
        }
      }
      next(error);
    }
  }

  /**
   * GET /documentos-flota/resumen-vencimientos
   * Get summary of document expirations by category
   */
  private async getResumenVencimientos(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 resumen = await this.documentoFlotaService.getResumenVencimientos(tenantId);
      res.json({ data: resumen });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /documentos-flota/entidades-bloqueadas
   * Get units and operators blocked due to expired critical documents
   */
  private async getEntidadesBloqueadas(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 entidadesBloqueadas = await this.documentoFlotaService.bloquearPorDocumentosVencidos(tenantId);
      res.json({ data: entidadesBloqueadas, total: entidadesBloqueadas.length });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /documentos-flota/alertas
   * Get document expiration alerts
   * Query param: dias (default: 30)
   */
  private async getAlertasVencimiento(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 { dias } = req.query;
      const diasAnticipacion = dias ? parseInt(dias as string, 10) : 30;

      if (isNaN(diasAnticipacion) || diasAnticipacion < 1) {
        res.status(400).json({ error: 'El parámetro días debe ser un número positivo' });
        return;
      }

      const alertas = await this.documentoFlotaService.sendAlertasVencimiento(tenantId, diasAnticipacion);
      res.json({
        data: alertas,
        total: alertas.length,
        criticos: alertas.filter(a => a.esCritico).length,
        diasConsultados: diasAnticipacion,
      });
    } catch (error) {
      next(error);
    }
  }
}
