import { Request, Response, NextFunction, Router } from 'express';
import { ViajesService, CreateViajeDto, UpdateViajeDto } from '../services/viajes.service';
import { EstadoViaje } from '../entities';

export class ViajesController {
  public router: Router;

  constructor(private readonly viajesService: ViajesService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // CRUD básico
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/en-progreso', this.getViajesEnProgreso.bind(this));
    this.router.get('/operador/:operadorId', this.getViajesOperador.bind(this));
    this.router.get('/unidad/:unidadId', this.getViajesUnidad.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.get('/numero/:numeroViaje', this.findByNumero.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.patch('/:id', this.update.bind(this));

    // Operaciones de estado
    this.router.post('/:id/estado', this.cambiarEstado.bind(this));
    this.router.post('/:id/despachar', this.despachar.bind(this));
    this.router.post('/:id/iniciar-transito', this.iniciarTransito.bind(this));
    this.router.post('/:id/registrar-llegada', this.registrarLlegada.bind(this));
    this.router.post('/:id/registrar-entrega', this.registrarEntrega.bind(this));
  }

  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 {
        search,
        estado,
        estados,
        unidadId,
        operadorId,
        clienteId,
        fechaDesde,
        fechaHasta,
        limit,
        offset,
      } = req.query;

      const result = await this.viajesService.findAll({
        tenantId,
        search: search as string,
        estado: estado as EstadoViaje,
        estados: estados ? (estados as string).split(',') as EstadoViaje[] : undefined,
        unidadId: unidadId as string,
        operadorId: operadorId as string,
        clienteId: clienteId as string,
        fechaDesde: fechaDesde ? new Date(fechaDesde as string) : undefined,
        fechaHasta: fechaHasta ? new Date(fechaHasta as string) : undefined,
        limit: limit ? parseInt(limit as string, 10) : undefined,
        offset: offset ? parseInt(offset as string, 10) : undefined,
      });

      res.json(result);
    } catch (error) {
      next(error);
    }
  }

  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 viaje = await this.viajesService.findOne(id, tenantId);

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

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

  private async findByNumero(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 { numeroViaje } = req.params;
      const viaje = await this.viajesService.findByNumero(numeroViaje, tenantId);

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

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

  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: CreateViajeDto = req.body;
      const viaje = await this.viajesService.create(tenantId, dto, userId);
      res.status(201).json({ data: viaje });
    } catch (error) {
      next(error);
    }
  }

  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: UpdateViajeDto = req.body;
      const viaje = await this.viajesService.update(id, tenantId, dto, userId);

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

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

  private async cambiarEstado(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 { estado } = req.body;

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

      const viaje = await this.viajesService.cambiarEstado(id, tenantId, estado, userId);

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

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

  private async despachar(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 { kmInicial, sellos } = req.body;

      const viaje = await this.viajesService.despachar(id, tenantId, { kmInicial, sellos }, userId);

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

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

  private async iniciarTransito(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 viaje = await this.viajesService.iniciarTransito(id, tenantId, userId);

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

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

  private async registrarLlegada(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 { kmFinal } = req.body;

      const viaje = await this.viajesService.registrarLlegada(id, tenantId, { kmFinal }, userId);

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

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

  private async registrarEntrega(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 { kmFinal, observaciones } = req.body;

      const viaje = await this.viajesService.registrarEntrega(id, tenantId, { kmFinal, observaciones }, userId);

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

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

  private async getViajesEnProgreso(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 viajes = await this.viajesService.getViajesEnProgreso(tenantId);
      res.json({ data: viajes });
    } catch (error) {
      next(error);
    }
  }

  private async getViajesOperador(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 viajes = await this.viajesService.getViajesOperador(operadorId, tenantId);
      res.json({ data: viajes });
    } catch (error) {
      next(error);
    }
  }

  private async getViajesUnidad(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 viajes = await this.viajesService.getViajesUnidad(unidadId, tenantId);
      res.json({ data: viajes });
    } catch (error) {
      next(error);
    }
  }
}
