import { Router, Request, Response, NextFunction } from 'express';
import { UserRole } from '@prisma/client';
import { canvassService } from './canvass.service';
import {
  recordVisitSchema,
  bulkRecordVisitSchema,
  startSessionSchema,
  walkingRouteSchema,
  listMyVisitsSchema,
  adminActivitySchema,
  adminVisitsSchema,
  volunteerUpdateLocationSchema,
  volunteerCreateLocationSchema,
  outcomeTrendsQuerySchema,
} from './canvass.schemas';
import { reverseGeocodeSchema, geocodeAddressSchema } from '../locations/locations.schemas';
import { locationsService } from '../locations/locations.service';
import { geocodingService } from '../geocoding/geocoding.service';
import { validate } from '../../../middleware/validate';
import { authenticate } from '../../../middleware/auth.middleware';
import { requireRole, requireNonTemp } from '../../../middleware/rbac.middleware';
import { canvassVisitRateLimit, canvassBulkVisitRateLimit, canvassGeocodeRateLimit } from '../../../middleware/rate-limit';
import { MAP_ROLES } from '../../../utils/roles';

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

// GET /api/map/canvass/my/assignments
volunteerRouter.get(
  '/my/assignments',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const assignments = await canvassService.getMyAssignments(req.user!.id);
      res.json(assignments);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/my/stats
volunteerRouter.get(
  '/my/stats',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const stats = await canvassService.getMyStats(req.user!.id);
      res.json(stats);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/my/visits
volunteerRouter.get(
  '/my/visits',
  validate(listMyVisitsSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await canvassService.getMyVisits(req.user!.id, req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

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

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

// POST /api/map/canvass/sessions/:id/end
volunteerRouter.post(
  '/sessions/:id/end',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const session = await canvassService.endSession(id, req.user!.id);
      res.json(session);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/cuts/:cutId/locations
volunteerRouter.get(
  '/cuts/:cutId/locations',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const cutId = req.params.cutId as string;
      const bounds = req.query.minLat ? {
        minLat: parseFloat(req.query.minLat as string),
        maxLat: parseFloat(req.query.maxLat as string),
        minLng: parseFloat(req.query.minLng as string),
        maxLng: parseFloat(req.query.maxLng as string),
      } : undefined;
      const limit = req.query.limit ? parseInt(req.query.limit as string) : undefined;
      const locations = await canvassService.getCutLocationsForCanvass(cutId, req.user!.id, bounds, limit);
      res.json(locations);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/cuts/:cutId/route
volunteerRouter.get(
  '/cuts/:cutId/route',
  validate(walkingRouteSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const cutId = req.params.cutId as string;
      const route = await canvassService.getWalkingRoute(cutId, req.user!.id, req.query as any);
      res.json(route);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/locations — all locations with visit annotations
volunteerRouter.get(
  '/locations',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const bounds = req.query.minLat ? {
        minLat: parseFloat(req.query.minLat as string),
        maxLat: parseFloat(req.query.maxLat as string),
        minLng: parseFloat(req.query.minLng as string),
        maxLng: parseFloat(req.query.maxLng as string),
      } : undefined;
      const limit = req.query.limit ? parseInt(req.query.limit as string) : undefined;
      const locations = await canvassService.getAllLocationsForCanvass(req.user!.id, bounds, limit);
      res.json(locations);
    } catch (err) {
      next(err);
    }
  },
);

// PUT /api/map/canvass/locations/:id — role-gated address editing (deprecated path, should be /addresses/:id)
volunteerRouter.put(
  '/locations/:id',
  validate(volunteerUpdateLocationSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const address = await canvassService.updateAddressAsVolunteer(
        id,
        req.user!.id,
        req.user!.role,
        req.body,
      );
      res.json(address);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/canvass/locations — create a new location (role-gated fields)
volunteerRouter.post(
  '/locations',
  validate(volunteerCreateLocationSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const data = { ...req.body };

      // Strip fields based on role
      const userRoles = req.user!.roles || [req.user!.role];
      const isAdmin = userRoles.some((r: string) => r === UserRole.SUPER_ADMIN || r === UserRole.MAP_ADMIN);
      if (!isAdmin) {
        delete data.firstName;
        delete data.lastName;
        delete data.email;
        delete data.phone;
      }
      if (userRoles.length === 1 && userRoles[0] === UserRole.TEMP) {
        delete data.supportLevel;
        delete data.sign;
        delete data.signSize;
        delete data.notes;
      }

      const location = await locationsService.create(data, req.user!.id);
      res.status(201).json(location);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/canvass/reverse-geocode — reverse geocode lat/lng
volunteerRouter.post(
  '/reverse-geocode',
  validate(reverseGeocodeSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await locationsService.reverseGeocode(req.body.latitude, req.body.longitude);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/canvass/geocode-search — geocode an address for map flyTo
volunteerRouter.post(
  '/geocode-search',
  canvassGeocodeRateLimit,
  validate(geocodeAddressSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await geocodingService.geocode(req.body.address);
      if (!result) {
        res.status(404).json({ error: { message: 'Address not found', code: 'GEOCODE_FAILED' } });
        return;
      }
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/canvass/visits
volunteerRouter.post(
  '/visits',
  canvassVisitRateLimit,
  validate(recordVisitSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const visit = await canvassService.recordVisit(req.user!.id, req.body);
      res.status(201).json(visit);
    } catch (err) {
      next(err);
    }
  },
);

// POST /api/map/canvass/visits/bulk - Record visit to all unvisited units in building
volunteerRouter.post(
  '/visits/bulk',
  canvassBulkVisitRateLimit, // Stricter rate limit for bulk operations
  validate(bulkRecordVisitSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await canvassService.recordBulkVisit(req.user!.id, req.body);
      res.status(201).json(result);
    } catch (err) {
      next(err);
    }
  },
);

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

// GET /api/map/canvass/stats
adminRouter.get(
  '/stats',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const stats = await canvassService.getAdminStats();
      res.json(stats);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/stats/cuts/:cutId
adminRouter.get(
  '/stats/cuts/:cutId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const cutId = req.params.cutId as string;
      const stats = await canvassService.getCutStats(cutId);
      res.json(stats);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/activity
adminRouter.get(
  '/activity',
  validate(adminActivitySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await canvassService.getAdminActivity(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/volunteers
adminRouter.get(
  '/volunteers',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const volunteers = await canvassService.getVolunteers();
      res.json(volunteers);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/volunteers/:userId — tightened 2026-04-12.
// Only SUPER_ADMIN or the subject volunteer can view per-volunteer canvass stats
// (which include visit locations and can reconstruct movement patterns).
adminRouter.get(
  '/volunteers/:userId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const userId = req.params.userId as string;
      if (req.user!.role !== 'SUPER_ADMIN' && userId !== req.user!.id) {
        const { AppError } = await import('../../../middleware/error-handler');
        throw new AppError(404, 'Volunteer not found', 'VOLUNTEER_NOT_FOUND');
      }
      const stats = await canvassService.getVolunteerStats(userId);
      res.json(stats);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/visits
adminRouter.get(
  '/visits',
  validate(adminVisitsSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await canvassService.getAdminVisits(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

// GET /api/map/canvass/trends
adminRouter.get(
  '/trends',
  validate(outcomeTrendsQuerySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await canvassService.getOutcomeTrends(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

export { volunteerRouter as canvassVolunteerRouter, adminRouter as canvassAdminRouter };
