import { Request, Response, NextFunction, Router } from 'express';
import { UnidadesService, CreateUnidadDto, UpdateUnidadDto } from '../services/unidades.service';
import { TipoUnidad, EstadoUnidad } from '../entities';

export class UnidadesController {
  public router: Router;

  constructor(private readonly unidadesService: UnidadesService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // CRUD básico
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/disponibles', this.getUnidadesDisponibles.bind(this));
    this.router.get('/seguro-por-vencer', this.getUnidadesSeguroPorVencer.bind(this));
    this.router.get('/verificacion-por-vencer', this.getUnidadesVerificacionPorVencer.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.get('/numero-economico/:numeroEconomico', this.findByNumeroEconomico.bind(this));
    this.router.get('/placas/:placas', this.findByPlacas.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/estado', this.cambiarEstado.bind(this));
    this.router.post('/:id/odometro', this.actualizarOdometro.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, tipo, estado, estados, sucursalId, limit, offset } = req.query;

      const result = await this.unidadesService.findAll({
        tenantId,
        search: search as string,
        tipo: tipo as TipoUnidad,
        estado: estado as EstadoUnidad,
        estados: estados ? (estados as string).split(',') as EstadoUnidad[] : undefined,
        sucursalId: sucursalId as string,
        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 unidad = await this.unidadesService.findOne(id, tenantId);

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

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

  private async findByNumeroEconomico(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 { numeroEconomico } = req.params;
      const unidad = await this.unidadesService.findByNumeroEconomico(numeroEconomico, tenantId);

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

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

  private async findByPlacas(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 { placas } = req.params;
      const unidad = await this.unidadesService.findByPlacas(placas, tenantId);

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

      res.json({ data: unidad });
    } 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: CreateUnidadDto = req.body;
      const unidad = await this.unidadesService.create(tenantId, dto, userId);
      res.status(201).json({ data: unidad });
    } 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: UpdateUnidadDto = req.body;
      const unidad = await this.unidadesService.update(id, tenantId, dto, userId);

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

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

  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.unidadesService.delete(id, tenantId);

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

      res.status(204).send();
    } 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 unidad = await this.unidadesService.cambiarEstado(id, tenantId, estado, userId);

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

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

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

      if (odometro === undefined) {
        res.status(400).json({ error: 'Odómetro es requerido' });
        return;
      }

      const unidad = await this.unidadesService.actualizarOdometro(id, tenantId, odometro, userId);

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

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

  private async getUnidadesDisponibles(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.query;
      const unidades = await this.unidadesService.getUnidadesDisponibles(tenantId, tipo as TipoUnidad);
      res.json({ data: unidades });
    } catch (error) {
      next(error);
    }
  }

  private async getUnidadesSeguroPorVencer(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;
      const unidades = await this.unidadesService.getUnidadesSeguroPorVencer(tenantId, diasAntelacion);
      res.json({ data: unidades });
    } catch (error) {
      next(error);
    }
  }

  private async getUnidadesVerificacionPorVencer(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;
      const unidades = await this.unidadesService.getUnidadesVerificacionPorVencer(tenantId, diasAntelacion);
      res.json({ data: unidades });
    } catch (error) {
      next(error);
    }
  }
}
