import { Router } from 'express';
import { authenticate } from '../../middleware/auth';
import { requireRoles, enforceSchoolScope } from '../../middleware/rbac';
import { validateBody } from '../../middleware/validate';
import { Role, ErrorCode } from '@saferoute/constants';
import { prisma } from '../../config/database';
import { sendSuccess, sendError, sendPaginated } from '../../utils/response';
import { createAuditLog } from '../../utils/audit-logger';
import { z } from 'zod';

export const schoolRoutes = Router();

const createSchoolSchema = z.object({
  name: z.string().min(1).max(255),
  address: z.string().min(1),
  city: z.string().min(1).max(100),
  state: z.string().min(1).max(100),
  pincode: z.string().min(5).max(10),
  contactPhone: z.string().min(10).max(15),
});

schoolRoutes.get('/', authenticate, requireRoles(Role.OWNER, Role.CTO, Role.OPERATIONS_MANAGER, Role.FINANCE_MANAGER), async (req, res) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 20;
    const skip = (page - 1) * limit;

    const [schools, total] = await Promise.all([
      prisma.school.findMany({ skip, take: limit, orderBy: { createdAt: 'desc' } }),
      prisma.school.count(),
    ]);

    const items = schools.map((s) => ({
      id: s.id, name: s.name, city: s.city, state: s.state, status: s.status,
      contactPhone: s.contactPhone, createdAt: s.createdAt.toISOString(),
    }));

    sendPaginated(res, items, total, page, limit);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list schools.');
  }
});

schoolRoutes.post('/', authenticate, requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER), validateBody(createSchoolSchema), async (req, res) => {
  try {
    const school = await prisma.school.create({ data: req.body });
    await createAuditLog({ actorId: req.user!.userId, action: 'CREATE_SCHOOL', entityType: 'school', entityId: school.id });
    sendSuccess(res, { id: school.id, name: school.name, city: school.city }, 'School created successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to create school.');
  }
});

schoolRoutes.get('/:schoolId', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const school = await prisma.school.findUnique({
      where: { id: req.params.schoolId },
      include: {
        _count: { select: { students: true, vehicles: true, routes: true, drivers: true } },
      },
    });
    if (!school) { sendError(res, ErrorCode.RESOURCE_NOT_FOUND, 'School not found.'); return; }

    sendSuccess(res, {
      id: school.id, name: school.name, address: school.address, city: school.city,
      state: school.state, pincode: school.pincode, contactPhone: school.contactPhone,
      status: school.status, createdAt: school.createdAt.toISOString(),
      counts: school._count,
    });
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to get school.');
  }
});

schoolRoutes.patch('/:schoolId', authenticate, requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER), async (req, res) => {
  try {
    const { name, address, city, state, pincode, contactPhone } = req.body;
    const school = await prisma.school.update({
      where: { id: req.params.schoolId },
      data: { ...(name && { name }), ...(address && { address }), ...(city && { city }), ...(state && { state }), ...(pincode && { pincode }), ...(contactPhone && { contactPhone }) },
    });
    sendSuccess(res, { id: school.id, name: school.name }, 'School updated successfully');
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to update school.');
  }
});

schoolRoutes.patch('/:schoolId/status', authenticate, requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER), async (req, res) => {
  try {
    const { status } = req.body;
    if (!['ACTIVE', 'INACTIVE', 'SUSPENDED'].includes(status)) {
      sendError(res, ErrorCode.VALIDATION_ERROR, 'Invalid status. Use: ACTIVE, INACTIVE, SUSPENDED');
      return;
    }
    const school = await prisma.school.update({ where: { id: req.params.schoolId }, data: { status } });
    await createAuditLog({ actorId: req.user!.userId, action: 'UPDATE_SCHOOL_STATUS', entityType: 'school', entityId: school.id, newValue: { status } });
    sendSuccess(res, { id: school.id, status: school.status }, 'School status updated');
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to update school status.');
  }
});

schoolRoutes.get('/:schoolId/students', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 20;
    const skip = (page - 1) * limit;

    const [students, total] = await Promise.all([
      prisma.student.findMany({
        where: { schoolId: req.params.schoolId },
        skip, take: limit, orderBy: { createdAt: 'desc' },
        include: { parents: { include: { user: { select: { id: true, fullName: true, phone: true } } } } },
      }),
      prisma.student.count({ where: { schoolId: req.params.schoolId } }),
    ]);

    sendPaginated(res, students.map((s) => ({
      id: s.id, fullName: s.fullName, classGrade: s.classGrade, section: s.section,
      rollNumber: s.rollNumber, isActive: s.isActive,
      parents: s.parents.map((p) => ({ id: p.id, name: p.user.fullName, phone: p.user.phone, relationship: p.relationship, isPrimary: p.isPrimary })),
    })), total, page, limit);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list students.');
  }
});

schoolRoutes.post('/:schoolId/students', authenticate, enforceSchoolScope(), requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER, Role.SCHOOL_PRINCIPAL, Role.SCHOOL_TRANSPORT_ADMIN), async (req, res) => {
  try {
    const { fullName, classGrade, section, rollNumber, photoUrl } = req.body;
    const student = await prisma.student.create({
      data: { schoolId: req.params.schoolId, fullName, classGrade, section, rollNumber, photoUrl },
    });
    sendSuccess(res, { id: student.id, fullName: student.fullName }, 'Student created successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to create student.');
  }
});

schoolRoutes.get('/:schoolId/vehicles', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const vehicles = await prisma.vehicle.findMany({
      where: { schoolId: req.params.schoolId },
      include: { gpsDevice: { select: { id: true, deviceImei: true, status: true } } },
      orderBy: { vehicleNumber: 'asc' },
    });
    sendSuccess(res, vehicles.map((v) => ({
      id: v.id, vehicleNumber: v.vehicleNumber, vehicleType: v.vehicleType,
      capacity: v.capacity, status: v.status, gpsDevice: v.gpsDevice,
    })));
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list vehicles.');
  }
});

schoolRoutes.post('/:schoolId/vehicles', authenticate, enforceSchoolScope(), requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER, Role.SCHOOL_TRANSPORT_ADMIN), async (req, res) => {
  try {
    const { vehicleNumber, vehicleType, capacity } = req.body;
    const vehicle = await prisma.vehicle.create({
      data: { schoolId: req.params.schoolId, vehicleNumber, vehicleType, capacity },
    });
    sendSuccess(res, { id: vehicle.id, vehicleNumber: vehicle.vehicleNumber }, 'Vehicle added successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to add vehicle.');
  }
});

schoolRoutes.get('/:schoolId/drivers', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const drivers = await prisma.driver.findMany({
      where: { schoolId: req.params.schoolId },
      include: { user: { select: { id: true, fullName: true, phone: true } }, vehicle: { select: { id: true, vehicleNumber: true } } },
    });
    sendSuccess(res, drivers.map((d) => ({
      id: d.id, fullName: d.user.fullName, phone: d.user.phone,
      licenseNumber: d.licenseNumber, vehicle: d.vehicle, isActive: d.isActive,
    })));
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list drivers.');
  }
});

schoolRoutes.post('/:schoolId/drivers', authenticate, enforceSchoolScope(), requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER, Role.SCHOOL_TRANSPORT_ADMIN), async (req, res) => {
  try {
    const { phone, fullName, licenseNumber, vehicleId } = req.body;

    let user = await prisma.user.findUnique({ where: { phone } });
    if (!user) { user = await prisma.user.create({ data: { phone, fullName } }); }

    const driverRole = await prisma.roleModel.findUnique({ where: { name: Role.DRIVER } });
    if (driverRole) {
      const existing = await prisma.userRole.findFirst({ where: { userId: user.id, roleId: driverRole.id } });
      if (!existing) {
        await prisma.userRole.create({ data: { userId: user.id, roleId: driverRole.id, schoolId: req.params.schoolId, assignedBy: req.user!.userId } });
      }
    }
    const driver = await prisma.driver.create({
      data: { userId: user.id, schoolId: req.params.schoolId, licenseNumber, vehicleId },
    });
    sendSuccess(res, { id: driver.id, fullName: user.fullName }, 'Driver added successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to add driver.');
  }
});

schoolRoutes.get('/:schoolId/attendants', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const attendants = await prisma.attendant.findMany({
      where: { schoolId: req.params.schoolId },
      include: { user: { select: { id: true, fullName: true, phone: true } }, vehicle: { select: { id: true, vehicleNumber: true } } },
    });
    sendSuccess(res, attendants.map((a) => ({
      id: a.id, fullName: a.user.fullName, phone: a.user.phone, vehicle: a.vehicle, isActive: a.isActive,
    })));
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list attendants.');
  }
});

schoolRoutes.post('/:schoolId/attendants', authenticate, enforceSchoolScope(), requireRoles(Role.OWNER, Role.OPERATIONS_MANAGER, Role.SCHOOL_TRANSPORT_ADMIN), async (req, res) => {
  try {
    const { phone, fullName, vehicleId } = req.body;
    let user = await prisma.user.findUnique({ where: { phone } });
    if (!user) { user = await prisma.user.create({ data: { phone, fullName } }); }
    const attendantRole = await prisma.roleModel.findUnique({ where: { name: Role.ATTENDANT } });
    if (attendantRole) {
      const existing = await prisma.userRole.findFirst({ where: { userId: user.id, roleId: attendantRole.id } });
      if (!existing) {
        await prisma.userRole.create({ data: { userId: user.id, roleId: attendantRole.id, schoolId: req.params.schoolId, assignedBy: req.user!.userId } });
      }
    }
    const attendant = await prisma.attendant.create({
      data: { userId: user.id, schoolId: req.params.schoolId, vehicleId },
    });
    sendSuccess(res, { id: attendant.id, fullName: user.fullName }, 'Attendant added successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to add attendant.');
  }
});

schoolRoutes.get('/:schoolId/routes', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const routes = await prisma.route.findMany({
      where: { schoolId: req.params.schoolId },
      include: {
        vehicle: { select: { id: true, vehicleNumber: true } },
        _count: { select: { stops: true, routeStudents: true } },
      },
      orderBy: { name: 'asc' },
    });
    sendSuccess(res, routes.map((r) => ({
      id: r.id, name: r.name, type: r.type, isActive: r.isActive,
      vehicle: r.vehicle, stopCount: r._count.stops, studentCount: r._count.routeStudents,
    })));
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list routes.');
  }
});

schoolRoutes.post('/:schoolId/routes', authenticate, enforceSchoolScope(), requireRoles(Role.OWNER, Role.SCHOOL_TRANSPORT_ADMIN, Role.ROUTE_COORDINATOR), async (req, res) => {
  try {
    const { name, type, vehicleId } = req.body;
    const route = await prisma.route.create({
      data: { schoolId: req.params.schoolId, name, type, vehicleId },
    });
    sendSuccess(res, { id: route.id, name: route.name }, 'Route created successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to create route.');
  }
});

schoolRoutes.get('/:schoolId/geofences', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const geofences = await prisma.geofence.findMany({ where: { schoolId: req.params.schoolId } });
    sendSuccess(res, geofences);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list geofences.');
  }
});

schoolRoutes.post('/:schoolId/geofences', authenticate, enforceSchoolScope(), requireRoles(Role.OWNER, Role.SCHOOL_PRINCIPAL, Role.ROUTE_COORDINATOR), async (req, res) => {
  try {
    const { name, type, centerLat, centerLng, radiusMeters } = req.body;
    const geofence = await prisma.geofence.create({
      data: { schoolId: req.params.schoolId, name, type, centerLat, centerLng, radiusMeters },
    });
    sendSuccess(res, { id: geofence.id, name: geofence.name }, 'Geofence created successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to create geofence.');
  }
});

schoolRoutes.get('/:schoolId/trips', authenticate, enforceSchoolScope(), async (req, res) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 20;
    const skip = (page - 1) * limit;

    const [trips, total] = await Promise.all([
      prisma.trip.findMany({
        where: { schoolId: req.params.schoolId },
        skip, take: limit, orderBy: { createdAt: 'desc' },
        include: {
          route: { select: { id: true, name: true } },
          vehicle: { select: { id: true, vehicleNumber: true } },
          driver: { include: { user: { select: { fullName: true } } } },
        },
      }),
      prisma.trip.count({ where: { schoolId: req.params.schoolId } }),
    ]);

    sendPaginated(res, trips.map((t) => ({
      id: t.id, tripDate: t.tripDate, type: t.type, status: t.status,
      route: t.route, vehicle: t.vehicle, driver: t.driver.user.fullName,
      startedAt: t.startedAt?.toISOString(), endedAt: t.endedAt?.toISOString(),
    })), total, page, limit);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list trips.');
  }
});
