import { Router } from 'express';
import { authenticate } from '../../middleware/auth';
import { requireRoles } from '../../middleware/rbac';
import { Role, ErrorCode } from '@saferoute/constants';
import { prisma } from '../../config/database';
import { sendSuccess, sendError } from '../../utils/response';

export const geofenceRoutes = Router();

geofenceRoutes.patch('/:geofenceId', authenticate, requireRoles(Role.OWNER, Role.SCHOOL_PRINCIPAL, Role.ROUTE_COORDINATOR), async (req, res) => {
  try {
    const { name, centerLat, centerLng, radiusMeters, isActive } = req.body;
    const geofence = await prisma.geofence.update({
      where: { id: req.params.geofenceId },
      data: { ...(name && { name }), ...(centerLat && { centerLat }), ...(centerLng && { centerLng }), ...(radiusMeters && { radiusMeters }), ...(isActive !== undefined && { isActive }) },
    });
    sendSuccess(res, { id: geofence.id, name: geofence.name }, 'Geofence updated');
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to update geofence.');
  }
});

geofenceRoutes.post('/check', authenticate, async (req, res) => {
  try {
    const { lat, lng, schoolId } = req.body;
    const geofences = await prisma.geofence.findMany({
      where: { schoolId, isActive: true },
    });

    const results = geofences.map((gf) => {
      const distance = haversineDistance(lat, lng, Number(gf.centerLat), Number(gf.centerLng));
      return {
        geofenceId: gf.id, name: gf.name, type: gf.type,
        distance: Math.round(distance), radiusMeters: gf.radiusMeters,
        isInside: distance <= gf.radiusMeters,
      };
    });

    sendSuccess(res, results);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to check geofence.');
  }
});

function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
  const R = 6371000; // Earth radius in meters
  const dLat = ((lat2 - lat1) * Math.PI) / 180;
  const dLon = ((lon2 - lon1) * Math.PI) / 180;
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) *
    Math.sin(dLon / 2) * Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  return R * c;
}
