import { Request, Response, NextFunction, Router } from 'express';
import {
  InspeccionPreViajeService,
  CreateInspeccionDto,
  UpdateInspeccionDto,
  DateRange,
} from '../services/inspeccion-pre-viaje.service';
import { ChecklistItem, FotoInspeccion } from '../entities';

/**
 * Controlador para gestion de inspecciones pre-viaje
 * NOM-087-SCT-2-2017 Compliance - Checklists de seguridad vehicular
 */
export class InspeccionPreViajeController {
  public router: Router;

  constructor(private readonly inspeccionService: InspeccionPreViajeService) {
    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));

    // Consultas por relaciones
    this.router.get('/viaje/:viajeId', this.findByViaje.bind(this));
    this.router.get('/unidad/:unidadId', this.findByUnidad.bind(this));
    this.router.get('/operador/:operadorId', this.findByOperador.bind(this));

    // Operaciones de checklist
    this.router.post('/:id/checklist-item', this.addChecklistItem.bind(this));
    this.router.patch('/:id/checklist-items', this.updateChecklistItems.bind(this));

    // Operaciones de finalizacion
    this.router.post('/:id/completar', this.completarInspeccion.bind(this));

    // Consultas especificas
    this.router.get('/:id/defectos', this.getDefectos.bind(this));
    this.router.get('/unidad/:unidadId/puede-despachar', this.canDespachar.bind(this));
    this.router.get('/unidad/:unidadId/ultima-aprobada', this.getUltimaAprobada.bind(this));

    // Fotos
    this.router.post('/:id/fotos', this.addFotos.bind(this));

    // Estadisticas
    this.router.get('/estadisticas/periodo', this.getEstadisticas.bind(this));
  }

  /**
   * GET / - Obtiene inspecciones (con filtros opcionales)
   */
  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, viajeId, desde, hasta } = req.query;

      let inspecciones;
      const dateRange: DateRange | undefined =
        desde && hasta
          ? { desde: new Date(desde as string), hasta: new Date(hasta as string) }
          : undefined;

      if (viajeId) {
        inspecciones = await this.inspeccionService.findByViaje(tenantId, viajeId as string);
      } else if (unidadId) {
        inspecciones = await this.inspeccionService.findByUnidad(
          tenantId,
          unidadId as string,
          dateRange
        );
      } else if (operadorId) {
        inspecciones = await this.inspeccionService.findByOperador(
          tenantId,
          operadorId as string,
          dateRange
        );
      } else {
        res.status(400).json({
          error: 'Se requiere al menos uno de: viajeId, unidadId, operadorId',
        });
        return;
      }

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

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

      if (!inspeccion) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

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

  /**
   * POST / - Crea una nueva inspeccion pre-viaje
   */
  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 { unidadId, operadorId, viajeId, remolqueId, fechaInspeccion, checklistItems, fotos } =
        req.body;

      if (!unidadId || !operadorId || !viajeId) {
        res.status(400).json({
          error: 'unidadId, operadorId y viajeId son requeridos',
        });
        return;
      }

      const dto: CreateInspeccionDto = {
        viajeId,
        remolqueId,
        fechaInspeccion: fechaInspeccion ? new Date(fechaInspeccion) : undefined,
        checklistItems,
        fotos,
      };

      const inspeccion = await this.inspeccionService.create(tenantId, unidadId, operadorId, dto);

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

  /**
   * PATCH /:id - Actualiza una inspeccion (checklist items y fotos)
   */
  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 { checklistItems } = req.body;

      if (!checklistItems || !Array.isArray(checklistItems)) {
        res.status(400).json({ error: 'checklistItems es requerido y debe ser un array' });
        return;
      }

      const inspeccion = await this.inspeccionService.updateChecklistItems(
        tenantId,
        id,
        checklistItems
      );

      if (!inspeccion) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

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

  /**
   * GET /viaje/:viajeId - Obtiene inspecciones de un viaje
   */
  private async findByViaje(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 { viajeId } = req.params;
      const inspecciones = await this.inspeccionService.findByViaje(tenantId, viajeId);

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

  /**
   * GET /unidad/:unidadId - Obtiene inspecciones de una unidad
   */
  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 { desde, hasta } = req.query;

      const dateRange: DateRange | undefined =
        desde && hasta
          ? { desde: new Date(desde as string), hasta: new Date(hasta as string) }
          : undefined;

      const inspecciones = await this.inspeccionService.findByUnidad(tenantId, unidadId, dateRange);

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

  /**
   * GET /operador/:operadorId - Obtiene inspecciones de un operador
   */
  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 { desde, hasta } = req.query;

      const dateRange: DateRange | undefined =
        desde && hasta
          ? { desde: new Date(desde as string), hasta: new Date(hasta as string) }
          : undefined;

      const inspecciones = await this.inspeccionService.findByOperador(
        tenantId,
        operadorId,
        dateRange
      );

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

  /**
   * POST /:id/checklist-item - Agrega o actualiza un item del checklist
   */
  private async addChecklistItem(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 item: ChecklistItem = req.body;

      if (!item.item || !item.categoria || !item.estado) {
        res.status(400).json({
          error: 'item, categoria y estado son requeridos',
        });
        return;
      }

      const inspeccion = await this.inspeccionService.addChecklistItem(tenantId, id, item);

      if (!inspeccion) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

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

  /**
   * PATCH /:id/checklist-items - Actualiza multiples items del checklist
   */
  private async updateChecklistItems(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 { items } = req.body;

      if (!items || !Array.isArray(items)) {
        res.status(400).json({ error: 'items es requerido y debe ser un array' });
        return;
      }

      const inspeccion = await this.inspeccionService.updateChecklistItems(tenantId, id, items);

      if (!inspeccion) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

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

  /**
   * POST /:id/completar - Completa una inspeccion
   */
  private async completarInspeccion(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 { firmaOperador } = req.body;

      const inspeccion = await this.inspeccionService.completeInspeccion(tenantId, id, firmaOperador);

      if (!inspeccion) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

      res.json({
        data: inspeccion,
        message: inspeccion.aprobada
          ? 'Inspeccion completada y aprobada'
          : 'Inspeccion completada pero NO aprobada debido a defectos criticos',
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /:id/defectos - Obtiene resumen de defectos de una inspeccion
   */
  private async getDefectos(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 defectos = await this.inspeccionService.getDefectos(tenantId, id);

      if (!defectos) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

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

  /**
   * GET /unidad/:unidadId/puede-despachar - Verifica si una unidad puede ser despachada
   */
  private async canDespachar(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 resultado = await this.inspeccionService.canDespachar(tenantId, unidadId);

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

  /**
   * GET /unidad/:unidadId/ultima-aprobada - Obtiene la ultima inspeccion aprobada
   */
  private async getUltimaAprobada(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 inspeccion = await this.inspeccionService.getUltimaInspeccionAprobada(
        tenantId,
        unidadId
      );

      if (!inspeccion) {
        res.status(404).json({ error: 'No hay inspecciones aprobadas para esta unidad' });
        return;
      }

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

  /**
   * POST /:id/fotos - Agrega fotos a una inspeccion
   */
  private async addFotos(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 { fotos } = req.body;

      if (!fotos || !Array.isArray(fotos)) {
        res.status(400).json({ error: 'fotos es requerido y debe ser un array' });
        return;
      }

      const inspeccion = await this.inspeccionService.addFotos(tenantId, id, fotos as FotoInspeccion[]);

      if (!inspeccion) {
        res.status(404).json({ error: 'Inspeccion pre-viaje no encontrada' });
        return;
      }

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

  /**
   * GET /estadisticas/periodo - Obtiene estadisticas de inspecciones por periodo
   */
  private async getEstadisticas(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 { desde, hasta } = req.query;

      if (!desde || !hasta) {
        res.status(400).json({ error: 'desde y hasta son requeridos' });
        return;
      }

      const dateRange: DateRange = {
        desde: new Date(desde as string),
        hasta: new Date(hasta as string),
      };

      const estadisticas = await this.inspeccionService.getEstadisticas(tenantId, dateRange);

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