/**
 * BranchSchedule Controller
 * API endpoints para gestión de horarios de sucursales
 *
 * @module Branches
 * @prefix /api/v1/branches/:branchId/schedules
 */

import { Router, Request, Response, NextFunction } from 'express';
import {
  BranchScheduleService,
  CreateBranchScheduleDto,
  UpdateBranchScheduleDto,
} from '../services/branch-schedule.service';
import { ServiceContext } from '../services/branch.service';

const router = Router({ mergeParams: true });
const branchScheduleService = new BranchScheduleService();

/**
 * Helper to extract ServiceContext from request
 */
function getContext(req: Request): ServiceContext {
  return {
    tenantId: req.headers['x-tenant-id'] as string,
    userId: (req as any).user?.id,
  };
}

/**
 * GET /api/v1/branches/:branchId/schedules
 * Lista todos los horarios de una sucursal
 */
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const { scheduleType, dayOfWeek, isActive } = req.query;

    const schedules = await branchScheduleService.findAll(getContext(req), {
      branchId,
      scheduleType: scheduleType as any,
      dayOfWeek: dayOfWeek ? parseInt(dayOfWeek as string) : undefined,
      isActive: isActive === 'true' ? true : isActive === 'false' ? false : undefined,
    });

    return res.json({
      success: true,
      data: schedules,
      count: schedules.length,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/branches/:branchId/schedules/regular
 * Obtiene los horarios regulares de una sucursal
 */
router.get('/regular', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const schedules = await branchScheduleService.findRegularSchedule(getContext(req), branchId);

    return res.json({ success: true, data: schedules, count: schedules.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/branches/:branchId/schedules/holidays
 * Obtiene los horarios de días festivos de una sucursal
 */
router.get('/holidays', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const schedules = await branchScheduleService.findHolidaySchedule(getContext(req), branchId);

    return res.json({ success: true, data: schedules, count: schedules.length });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/branches/:branchId/schedules/for-date
 * Obtiene el horario efectivo para una fecha específica
 */
router.get('/for-date', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const { date } = req.query;

    if (!date) {
      return res.status(400).json({ error: 'date es requerido (formato: YYYY-MM-DD)' });
    }

    const schedule = await branchScheduleService.findScheduleForDate(
      getContext(req),
      branchId,
      new Date(date as string)
    );

    if (!schedule) {
      return res.status(404).json({ error: 'No hay horario configurado para esta fecha' });
    }

    return res.json({ success: true, data: schedule });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/branches/:branchId/schedules/is-open
 * Verifica si la sucursal está abierta en un momento dado
 */
router.get('/is-open', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const { datetime } = req.query;

    const checkDate = datetime ? new Date(datetime as string) : new Date();
    const isOpen = await branchScheduleService.isBranchOpenAt(getContext(req), branchId, checkDate);

    return res.json({
      success: true,
      data: {
        isOpen,
        checkedAt: checkDate.toISOString(),
      },
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/branches/:branchId/schedules/:id
 * Obtiene un horario específico
 */
router.get('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const schedule = await branchScheduleService.findById(getContext(req), req.params.id);
    if (!schedule) {
      return res.status(404).json({ error: 'Horario no encontrado' });
    }

    return res.json({ success: true, data: schedule });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/branches/:branchId/schedules
 * Crea un nuevo horario
 */
router.post('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const data: CreateBranchScheduleDto = {
      ...req.body,
      branchId,
    };

    // Validate required fields
    if (!data.name || !data.openTime || !data.closeTime) {
      return res.status(400).json({ error: 'name, openTime y closeTime son requeridos' });
    }

    const schedule = await branchScheduleService.create(getContext(req), data);
    return res.status(201).json({ success: true, data: schedule });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/branches/:branchId/schedules/weekly
 * Crea o reemplaza el horario semanal completo
 */
router.post('/weekly', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { branchId } = req.params;
    const { weeklyHours } = req.body;

    if (!weeklyHours || !Array.isArray(weeklyHours)) {
      return res.status(400).json({
        error: 'weeklyHours es requerido como array de { dayOfWeek, openTime, closeTime, shifts? }',
      });
    }

    const schedules = await branchScheduleService.createWeeklySchedule(
      getContext(req),
      branchId,
      weeklyHours
    );

    return res.status(201).json({
      success: true,
      data: schedules,
      count: schedules.length,
      message: 'Horario semanal configurado',
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * PATCH /api/v1/branches/:branchId/schedules/:id
 * Actualiza un horario
 */
router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data: UpdateBranchScheduleDto = req.body;
    const schedule = await branchScheduleService.update(getContext(req), req.params.id, data);

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

    return res.json({ success: true, data: schedule });
  } catch (error) {
    return next(error);
  }
});

/**
 * DELETE /api/v1/branches/:branchId/schedules/:id
 * Elimina un horario
 */
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const deleted = await branchScheduleService.delete(getContext(req), req.params.id);
    if (!deleted) {
      return res.status(404).json({ error: 'Horario no encontrado' });
    }

    return res.json({ success: true, message: 'Horario eliminado' });
  } catch (error) {
    return next(error);
  }
});

export default router;
