import { Router, Request, Response, NextFunction } from 'express';
import { trackingService } from './tracking.service';
import {
  startTrackingSchema,
  submitPointsSchema,
  linkCanvassSchema,
  liveVolunteersQuerySchema,
  historicalListQuerySchema,
  myHistoricalListQuerySchema,
} from './tracking.schemas';
import { validate } from '../../../middleware/validate';
import { authenticate } from '../../../middleware/auth.middleware';
import { requireRole } from '../../../middleware/rbac.middleware';
import { gpsTrackingRateLimit } from '../../../middleware/rate-limit';
import { MAP_ROLES } from '../../../utils/roles';
import { UserRole } from '@prisma/client';
import { AppError } from '../../../middleware/error-handler';
import { prisma } from '../../../config/database';

// ─── Volunteer Router ────────────────────────────────────────────────
const volunteerRouter = Router();
volunteerRouter.use(authenticate);

// POST /api/map/tracking/sessions — start tracking session
volunteerRouter.post(
  '/sessions',
  validate(startTrackingSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const session = await trackingService.startSession(req.user!.id, req.body);
      res.status(201).json(session);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/tracking/sessions/:id/end — end tracking session
volunteerRouter.post(
  '/sessions/:id/end',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const session = await trackingService.endSession(id, req.user!.id);
      if (!session) {
        res.status(404).json({ error: { message: 'Active tracking session not found', code: 'NOT_FOUND' } });
        return;
      }
      res.json(session);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/tracking/sessions/:id/points — submit GPS point batch
volunteerRouter.post(
  '/sessions/:id/points',
  gpsTrackingRateLimit,
  validate(submitPointsSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await trackingService.submitPoints(id, req.user!.id, req.body);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/tracking/sessions/:id/link-canvass — link to canvass session
volunteerRouter.post(
  '/sessions/:id/link-canvass',
  validate(linkCanvassSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const session = await trackingService.linkCanvassSession(id, req.user!.id, req.body);
      res.json(session);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/tracking/my/session — get active tracking session
volunteerRouter.get(
  '/my/session',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const session = await trackingService.getActiveSession(req.user!.id);
      res.json(session);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/tracking/my/sessions — paginated list of own tracking sessions
volunteerRouter.get(
  '/my/sessions',
  validate(myHistoricalListQuerySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const query = req.query as any;
      const result = await trackingService.getHistoricalSessions({
        ...query,
        userId: req.user!.id,
      });
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/tracking/my/sessions/:id/route — full route for own session
volunteerRouter.get(
  '/my/sessions/:id/route',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const route = await trackingService.getMySessionRoute(id, req.user!.id);
      if (!route) {
        res.status(404).json({ error: { message: 'Session not found', code: 'NOT_FOUND' } });
        return;
      }
      res.json(route);
    } catch (err) {
      next(err);
    }
  },
);

// ─── Admin Router ────────────────────────────────────────────────────
const adminRouter = Router();
adminRouter.use(authenticate);
adminRouter.use(requireRole(...MAP_ROLES));

// GET /api/map/tracking/live — active volunteers with positions + recent trails
adminRouter.get(
  '/live',
  validate(liveVolunteersQuerySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const volunteers = await trackingService.getLiveVolunteers(req.query as any);
      res.json(volunteers);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/tracking/sessions — historical session list
adminRouter.get(
  '/sessions',
  validate(historicalListQuerySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await trackingService.getHistoricalSessions(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/tracking/sessions/:id/route — full GPS route for a session.
// Tightened 2026-04-12: only SUPER_ADMIN or the session's owning volunteer can
// view raw GPS traces. Previously any MAP_ADMIN could enumerate any volunteer's
// movements, which is an unacceptable privacy risk for a political platform.
adminRouter.get(
  '/sessions/:id/route',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const session = await prisma.trackingSession.findUnique({
        where: { id },
        select: { userId: true },
      });
      if (!session) throw new AppError(404, 'Session not found', 'SESSION_NOT_FOUND');
      if (req.user!.role !== UserRole.SUPER_ADMIN && session.userId !== req.user!.id) {
        // 404 not 403 to avoid confirming session existence for unauthorized admins.
        throw new AppError(404, 'Session not found', 'SESSION_NOT_FOUND');
      }
      const route = await trackingService.getSessionRoute(id);
      res.json(route);
    } catch (err) {
      next(err);
    }
  },
);

export { volunteerRouter as trackingVolunteerRouter, adminRouter as trackingAdminRouter };
