import { Router, Request, Response, NextFunction } from 'express';
import { shiftsService } from './shifts.service';
import {
  createShiftSchema,
  updateShiftSchema,
  listShiftsSchema,
  addSignupSchema,
  publicSignupSchema,
} from './shifts.schemas';
import { validate } from '../../../middleware/validate';
import { authenticate } from '../../../middleware/auth.middleware';
import { requireRole } from '../../../middleware/rbac.middleware';
import { shiftSignupRateLimit } from '../../../middleware/rate-limit';
import { prisma } from '../../../config/database';
import { redis } from '../../../config/redis';
import { SCHEDULING_ROLES } from '../../../utils/roles';

// --- Admin Router ---
const adminRouter = Router();
adminRouter.use(authenticate);
adminRouter.use(requireRole(...SCHEDULING_ROLES));

// GET /api/map/shifts — list paginated
adminRouter.get(
  '/',
  validate(listShiftsSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await shiftsService.findAll(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/map/shifts/stats — statistics
adminRouter.get(
  '/stats',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const stats = await shiftsService.getStats();
      res.json(stats);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/map/shifts/calendar — calendar data
adminRouter.get(
  '/calendar',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { startDate, endDate } = req.query;

      if (!startDate || !endDate) {
        return res.status(400).json({ error: 'startDate and endDate required' });
      }

      const data = await shiftsService.getCalendarData(
        startDate as string,
        endDate as string
      );

      res.json(data);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/map/shifts/:id — single shift with signups
adminRouter.get(
  '/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const shift = await shiftsService.findById(id);
      res.json(shift);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/map/shifts — create
adminRouter.post(
  '/',
  validate(createShiftSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const shift = await shiftsService.create(req.body, req.user!.id);
      res.status(201).json(shift);
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/map/shifts/:id — update
adminRouter.put(
  '/:id',
  validate(updateShiftSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const shift = await shiftsService.update(id, req.body);
      res.json(shift);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/map/shifts/:id — delete
adminRouter.delete(
  '/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await shiftsService.delete(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/map/shifts/:id/signups — admin add volunteer
adminRouter.post(
  '/:id/signups',
  validate(addSignupSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const signup = await shiftsService.addSignup(id, req.body);
      res.status(201).json(signup);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/map/shifts/:id/signups/:signupId — admin remove volunteer
adminRouter.delete(
  '/:id/signups/:signupId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const signupId = req.params.signupId as string;
      await shiftsService.removeSignup(signupId);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/map/shifts/:id/meeting — create and link a video briefing meeting
adminRouter.post(
  '/:id/meeting',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const meeting = await shiftsService.createMeetingForShift(id, req.user!.id);
      res.status(201).json(meeting);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/map/shifts/:id/meeting — remove video briefing from shift
adminRouter.delete(
  '/:id/meeting',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await shiftsService.removeMeetingFromShift(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/map/shifts/:id/email-details — email all volunteers
adminRouter.post(
  '/:id/email-details',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await shiftsService.emailShiftDetails(id);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

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

// GET /api/map/shifts/volunteer/upcoming — upcoming shifts with signup status
volunteerRouter.get(
  '/volunteer/upcoming',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const shifts = await shiftsService.getUpcomingForVolunteer(req.user!.id);
      res.json(shifts);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/map/shifts/volunteer/my-signups — own confirmed signups
volunteerRouter.get(
  '/volunteer/my-signups',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const signups = await shiftsService.getMySignups(req.user!.id);
      res.json(signups);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/map/shifts/volunteer/:id/signup — sign up for shift
volunteerRouter.post(
  '/volunteer/:id/signup',
  shiftSignupRateLimit,
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const signup = await shiftsService.volunteerSignup(id, req.user!.id);
      res.status(201).json(signup);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/map/shifts/volunteer/:id/signup — cancel own signup
volunteerRouter.delete(
  '/volunteer/:id/signup',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await shiftsService.cancelVolunteerSignup(id, req.user!.id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// --- Public Router ---
const publicRouter = Router();

// GET /api/map/shifts/public?page=1&limit=20 — list upcoming public shifts (paginated)
publicRouter.get(
  '/public',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const page = Math.max(parseInt(req.query.page as string) || 1, 1);
      const limit = Math.min(parseInt(req.query.limit as string) || 20, 50);
      const result = await shiftsService.getPublicShifts(page, limit);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/map/shifts/public/:id/signup — public signup
publicRouter.post(
  '/public/:id/signup',
  shiftSignupRateLimit,
  validate(publicSignupSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await shiftsService.publicSignup(id, req.body);
      res.status(201).json(result);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/map/shifts/public/related — related active campaigns
publicRouter.get(
  '/public/related',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const cacheKey = 'shifts:related:campaigns';
      try {
        const cached = await redis.get(cacheKey);
        if (cached) { res.json(JSON.parse(cached)); return; }
      } catch { /* cache miss */ }

      const campaigns = await prisma.campaign.findMany({
        where: { status: 'ACTIVE' },
        select: {
          id: true,
          slug: true,
          title: true,
          description: true,
          _count: { select: { emails: true } },
        },
        orderBy: { createdAt: 'desc' },
        take: 3,
      });

      const result = {
        campaigns: campaigns.map(c => ({
          id: c.id,
          slug: c.slug,
          title: c.title,
          description: c.description?.slice(0, 150) ?? null,
          emailCount: c._count.emails,
        })),
      };

      try { await redis.setex(cacheKey, 300, JSON.stringify(result)); } catch { /* non-critical */ }

      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

export { adminRouter as shiftsAdminRouter, publicRouter as shiftsPublicRouter, volunteerRouter as shiftsVolunteerRouter };
