// src/routes/admin.routes.ts
import { Router, NextFunction, Request, Response } from 'express';
import passport, { isAdmin } from '../config/passport';
import { z } from 'zod';

import * as db from '../services/db/index.db';
import type { UserProfile } from '../types';
import { geocodingService } from '../services/geocodingService.server';
import { cacheService } from '../services/cacheService.server';
import { requireFileUpload } from '../middleware/fileUpload.middleware'; // This was a duplicate, fixed.
import { createUploadMiddleware, handleMulterError } from '../middleware/multer.middleware';
import { ValidationError } from '../services/db/errors.db';
import { validateRequest } from '../middleware/validation.middleware';

// --- Bull Board (Job Queue UI) Imports ---
import { createBullBoard } from '@bull-board/api';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { ExpressAdapter } from '@bull-board/express';
import { backgroundJobService } from '../services/backgroundJobService';
import {
  flyerQueue,
  emailQueue,
  analyticsQueue,
  cleanupQueue,
  weeklyAnalyticsQueue,
} from '../services/queueService.server';
import { numericIdParam, uuidParamSchema, optionalNumeric } from '../utils/zodUtils';
// Removed: import { logger } from '../services/logger.server';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { monitoringService } from '../services/monitoringService.server';
import { userService } from '../services/userService';
import { cleanupUploadedFile } from '../utils/fileUtils';
import { brandService } from '../services/brandService';
import { adminTriggerLimiter, adminUploadLimiter } from '../config/rateLimiters';
import { sendSuccess, sendNoContent } from '../utils/apiResponse';

const updateCorrectionSchema = numericIdParam('id').extend({
  body: z.object({
    suggested_value: z.string().trim().min(1, 'A new suggested_value is required.'),
  }),
});

const updateRecipeStatusSchema = numericIdParam('id').extend({
  body: z.object({
    status: z.enum(['private', 'pending_review', 'public', 'rejected']),
  }),
});

const updateCommentStatusSchema = numericIdParam('id').extend({
  body: z.object({
    status: z.enum(['visible', 'hidden', 'reported']),
  }),
});

const updateUserRoleSchema = uuidParamSchema('id', 'A valid user ID is required.').extend({
  body: z.object({
    role: z.enum(['user', 'admin']),
  }),
});

const activityLogSchema = z.object({
  query: z.object({
    limit: optionalNumeric({ default: 50, integer: true, positive: true }),
    offset: optionalNumeric({ default: 0, integer: true, nonnegative: true }),
  }),
});

const usersListSchema = z.object({
  query: z.object({
    limit: optionalNumeric({ integer: true, positive: true, max: 100 }),
    offset: optionalNumeric({ default: 0, integer: true, nonnegative: true }),
  }),
});

const jobRetrySchema = z.object({
  params: z.object({
    queueName: z.enum([
      'flyer-processing',
      'email-sending',
      'analytics-reporting',
      'file-cleanup',
      'weekly-analytics-reporting',
    ]),
    jobId: z.string().trim().min(1, 'A valid Job ID is required.'),
  }),
});

const emptySchema = z.object({});

const router = Router();

const brandLogoUpload = createUploadMiddleware({
  storageType: 'flyer', // Using flyer storage path is acceptable for brand logos.
  fileSize: 2 * 1024 * 1024, // 2MB limit for logos
  fileFilter: 'image',
});

// --- Bull Board (Job Queue UI) Setup ---
const serverAdapter = new ExpressAdapter();
serverAdapter.setBasePath('/api/admin/jobs'); // Set the base path for the UI

createBullBoard({
  queues: [
    new BullMQAdapter(flyerQueue),
    new BullMQAdapter(emailQueue),
    new BullMQAdapter(analyticsQueue),
    new BullMQAdapter(cleanupQueue),
    new BullMQAdapter(weeklyAnalyticsQueue), // Add the weekly analytics queue to the board
  ],
  options: {
    uiConfig: {
      boardTitle: 'Bull Dashboard',
    },
  },
  serverAdapter: serverAdapter,
});

// Mount the Bull Board UI router. This must be done BEFORE the isAdmin middleware
// so the UI's own assets can be served, but the routes within it will be protected
// by the router-level `isAdmin` middleware below.
router.use('/jobs', serverAdapter.getRouter());

// --- Middleware for all admin routes ---
router.use(passport.authenticate('jwt', { session: false }), isAdmin);

// --- Admin Routes ---

/**
 * @openapi
 * /admin/corrections:
 *   get:
 *     tags: [Admin]
 *     summary: Get suggested corrections
 *     description: Retrieve all suggested corrections for review. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of suggested corrections
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/SuccessResponse'
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get('/corrections', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
  try {
    const corrections = await db.adminRepo.getSuggestedCorrections(req.log);
    sendSuccess(res, corrections);
  } catch (error) {
    req.log.error({ error }, 'Error fetching suggested corrections');
    next(error);
  }
});

/**
 * @openapi
 * /admin/review/flyers:
 *   get:
 *     tags: [Admin]
 *     summary: Get flyers for review
 *     description: Retrieve flyers pending admin review. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of flyers for review
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get('/review/flyers', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
  try {
    req.log.debug('Fetching flyers for review via adminRepo');
    const flyers = await db.adminRepo.getFlyersForReview(req.log);
    req.log.info(
      { count: Array.isArray(flyers) ? flyers.length : 'unknown' },
      'Successfully fetched flyers for review',
    );
    sendSuccess(res, flyers);
  } catch (error) {
    req.log.error({ error }, 'Error fetching flyers for review');
    next(error);
  }
});

/**
 * @openapi
 * /admin/brands:
 *   get:
 *     tags: [Admin]
 *     summary: Get all brands
 *     description: Retrieve all brands. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of brands
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get('/brands', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
  try {
    const brands = await db.flyerRepo.getAllBrands(req.log);
    sendSuccess(res, brands);
  } catch (error) {
    req.log.error({ error }, 'Error fetching brands');
    next(error);
  }
});

/**
 * @openapi
 * /admin/stats:
 *   get:
 *     tags: [Admin]
 *     summary: Get application stats
 *     description: Retrieve overall application statistics. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Application statistics
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get('/stats', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
  try {
    const stats = await db.adminRepo.getApplicationStats(req.log);
    sendSuccess(res, stats);
  } catch (error) {
    req.log.error({ error }, 'Error fetching application stats');
    next(error);
  }
});

/**
 * @openapi
 * /admin/stats/daily:
 *   get:
 *     tags: [Admin]
 *     summary: Get daily statistics
 *     description: Retrieve daily statistics for the last 30 days. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Daily statistics for last 30 days
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get('/stats/daily', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
  try {
    const dailyStats = await db.adminRepo.getDailyStatsForLast30Days(req.log);
    sendSuccess(res, dailyStats);
  } catch (error) {
    req.log.error({ error }, 'Error fetching daily stats');
    next(error);
  }
});

/**
 * @openapi
 * /admin/corrections/{id}/approve:
 *   post:
 *     tags: [Admin]
 *     summary: Approve a correction
 *     description: Approve a suggested correction. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: Correction ID
 *     responses:
 *       200:
 *         description: Correction approved successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Correction not found
 */
router.post(
  '/corrections/:id/approve',
  validateRequest(numericIdParam('id')),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParam>>;
    try {
      await db.adminRepo.approveCorrection(params.id, req.log); // params.id is now safely typed as number
      sendSuccess(res, { message: 'Correction approved successfully.' });
    } catch (error) {
      req.log.error({ error }, 'Error approving correction');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/corrections/{id}/reject:
 *   post:
 *     tags: [Admin]
 *     summary: Reject a correction
 *     description: Reject a suggested correction. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: Correction ID
 *     responses:
 *       200:
 *         description: Correction rejected successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Correction not found
 */
router.post(
  '/corrections/:id/reject',
  validateRequest(numericIdParam('id')),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParam>>;
    try {
      await db.adminRepo.rejectCorrection(params.id, req.log); // params.id is now safely typed as number
      sendSuccess(res, { message: 'Correction rejected successfully.' });
    } catch (error) {
      req.log.error({ error }, 'Error rejecting correction');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/corrections/{id}:
 *   put:
 *     tags: [Admin]
 *     summary: Update a correction
 *     description: Update a suggested correction's value. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: Correction ID
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - suggested_value
 *             properties:
 *               suggested_value:
 *                 type: string
 *                 description: New suggested value
 *     responses:
 *       200:
 *         description: Correction updated successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Correction not found
 */
router.put(
  '/corrections/:id',
  validateRequest(updateCorrectionSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params, body } = req as unknown as z.infer<typeof updateCorrectionSchema>;
    try {
      const updatedCorrection = await db.adminRepo.updateSuggestedCorrection(
        params.id,
        body.suggested_value,
        req.log,
      );
      sendSuccess(res, updatedCorrection);
    } catch (error) {
      req.log.error({ error }, 'Error updating suggested correction');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/recipes/{id}/status:
 *   put:
 *     tags: [Admin]
 *     summary: Update recipe status
 *     description: Update a recipe's publication status. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: Recipe ID
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - status
 *             properties:
 *               status:
 *                 type: string
 *                 enum: [private, pending_review, public, rejected]
 *     responses:
 *       200:
 *         description: Recipe status updated successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Recipe not found
 */
router.put(
  '/recipes/:id/status',
  validateRequest(updateRecipeStatusSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params, body } = req as unknown as z.infer<typeof updateRecipeStatusSchema>;
    try {
      const updatedRecipe = await db.adminRepo.updateRecipeStatus(params.id, body.status, req.log); // This is still a standalone function in admin.db.ts
      sendSuccess(res, updatedRecipe);
    } catch (error) {
      req.log.error({ error }, 'Error updating recipe status');
      next(error); // Pass all errors to the central error handler
    }
  },
);

/**
 * @openapi
 * /admin/brands/{id}/logo:
 *   post:
 *     tags: [Admin]
 *     summary: Upload brand logo
 *     description: Upload or update a brand's logo image. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: Brand ID
 *     requestBody:
 *       required: true
 *       content:
 *         multipart/form-data:
 *           schema:
 *             type: object
 *             required:
 *               - logoImage
 *             properties:
 *               logoImage:
 *                 type: string
 *                 format: binary
 *                 description: Logo image file (max 2MB)
 *     responses:
 *       200:
 *         description: Brand logo updated successfully
 *       400:
 *         description: Invalid file or missing logo image
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Brand not found
 */
router.post(
  '/brands/:id/logo',
  adminUploadLimiter,
  validateRequest(numericIdParam('id')),
  brandLogoUpload.single('logoImage'),
  requireFileUpload('logoImage'),
  async (req: Request, res: Response, next: NextFunction) => {
    const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParam>>;
    try {
      // Although requireFileUpload middleware should ensure the file exists,
      // this check satisfies TypeScript and adds robustness.
      if (!req.file) {
        throw new ValidationError([], 'Logo image file is missing.');
      }

      const logoUrl = await brandService.updateBrandLogo(params.id, req.file, req.log);

      req.log.info(
        { brandId: params.id, logoUrl },
        `Brand logo updated for brand ID: ${params.id}`,
      );
      sendSuccess(res, { message: 'Brand logo updated successfully.', logoUrl });
    } catch (error) {
      // If an error occurs after the file has been uploaded (e.g., DB error),
      // we must clean up the orphaned file from the disk.
      await cleanupUploadedFile(req.file);
      req.log.error({ error }, 'Error updating brand logo');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/unmatched-items:
 *   get:
 *     tags: [Admin]
 *     summary: Get unmatched flyer items
 *     description: Retrieve flyer items that couldn't be matched to master items. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of unmatched flyer items
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get(
  '/unmatched-items',
  validateRequest(emptySchema),
  async (req, res, next: NextFunction) => {
    try {
      const items = await db.adminRepo.getUnmatchedFlyerItems(req.log);
      sendSuccess(res, items);
    } catch (error) {
      req.log.error({ error }, 'Error fetching unmatched items');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/recipes/{recipeId}:
 *   delete:
 *     tags: [Admin]
 *     summary: Delete a recipe
 *     description: Admin endpoint to delete any recipe. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: recipeId
 *         required: true
 *         schema:
 *           type: integer
 *         description: Recipe ID
 *     responses:
 *       204:
 *         description: Recipe deleted successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Recipe not found
 */
router.delete(
  '/recipes/:recipeId',
  validateRequest(numericIdParam('recipeId')),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    // Infer the type directly from the schema generator function. // This was a duplicate, fixed.
    const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParam>>;
    try {
      // The isAdmin flag bypasses the ownership check in the repository method.
      await db.recipeRepo.deleteRecipe(params.recipeId, userProfile.user.user_id, true, req.log);
      sendNoContent(res);
    } catch (error: unknown) {
      req.log.error({ error }, 'Error deleting recipe');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/flyers/{flyerId}:
 *   delete:
 *     tags: [Admin]
 *     summary: Delete a flyer
 *     description: Admin endpoint to delete a flyer and its items. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: flyerId
 *         required: true
 *         schema:
 *           type: integer
 *         description: Flyer ID
 *     responses:
 *       204:
 *         description: Flyer deleted successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Flyer not found
 */
router.delete(
  '/flyers/:flyerId',
  validateRequest(numericIdParam('flyerId')),
  async (req: Request, res: Response, next: NextFunction) => {
    // Infer the type directly from the schema generator function.
    const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParam>>;
    try {
      await db.flyerRepo.deleteFlyer(params.flyerId, req.log);
      sendNoContent(res);
    } catch (error: unknown) {
      req.log.error({ error }, 'Error deleting flyer');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/comments/{id}/status:
 *   put:
 *     tags: [Admin]
 *     summary: Update comment status
 *     description: Update a recipe comment's visibility status. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *         description: Comment ID
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - status
 *             properties:
 *               status:
 *                 type: string
 *                 enum: [visible, hidden, reported]
 *     responses:
 *       200:
 *         description: Comment status updated successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Comment not found
 */
router.put(
  '/comments/:id/status',
  validateRequest(updateCommentStatusSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params, body } = req as unknown as z.infer<typeof updateCommentStatusSchema>;
    try {
      const updatedComment = await db.adminRepo.updateRecipeCommentStatus(
        params.id,
        body.status,
        req.log,
      ); // This is still a standalone function in admin.db.ts
      sendSuccess(res, updatedComment);
    } catch (error: unknown) {
      req.log.error({ error }, 'Error updating comment status');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/users:
 *   get:
 *     tags: [Admin]
 *     summary: Get all users
 *     description: Retrieve a list of all users with optional pagination. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           maximum: 100
 *         description: Maximum number of users to return. If omitted, returns all users.
 *       - in: query
 *         name: offset
 *         schema:
 *           type: integer
 *           default: 0
 *         description: Number of users to skip
 *     responses:
 *       200:
 *         description: List of users with total count
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get('/users', validateRequest(usersListSchema), async (req, res, next: NextFunction) => {
  try {
    const { limit, offset } = usersListSchema.shape.query.parse(req.query);
    const result = await db.adminRepo.getAllUsers(req.log, limit, offset);
    sendSuccess(res, result);
  } catch (error) {
    req.log.error({ error }, 'Error fetching users');
    next(error);
  }
});

/**
 * @openapi
 * /admin/activity-log:
 *   get:
 *     tags: [Admin]
 *     summary: Get activity log
 *     description: Retrieve system activity log with pagination. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 50
 *         description: Maximum number of entries to return
 *       - in: query
 *         name: offset
 *         schema:
 *           type: integer
 *           default: 0
 *         description: Number of entries to skip
 *     responses:
 *       200:
 *         description: Activity log entries
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get(
  '/activity-log',
  validateRequest(activityLogSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety.
    // We parse the query here to apply Zod's coercions (string to number) and defaults.
    const { limit, offset } = activityLogSchema.shape.query.parse(req.query);

    try {
      const logs = await db.adminRepo.getActivityLog(limit!, offset!, req.log);
      sendSuccess(res, logs);
    } catch (error) {
      req.log.error({ error }, 'Error fetching activity log');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/users/{id}:
 *   get:
 *     tags: [Admin]
 *     summary: Get user by ID
 *     description: Retrieve a specific user's profile. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *           format: uuid
 *         description: User ID
 *     responses:
 *       200:
 *         description: User profile
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: User not found
 */
router.get(
  '/users/:id',
  validateRequest(uuidParamSchema('id', 'A valid user ID is required.')),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params } = req as unknown as z.infer<ReturnType<typeof uuidParamSchema>>;
    try {
      const user = await db.userRepo.findUserProfileById(params.id, req.log);
      sendSuccess(res, user);
    } catch (error) {
      req.log.error({ error }, 'Error fetching user profile');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/users/{id}:
 *   put:
 *     tags: [Admin]
 *     summary: Update user role
 *     description: Update a user's role. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *           format: uuid
 *         description: User ID
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - role
 *             properties:
 *               role:
 *                 type: string
 *                 enum: [user, admin]
 *     responses:
 *       200:
 *         description: User role updated successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: User not found
 */
router.put(
  '/users/:id',
  validateRequest(updateUserRoleSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    // Apply ADR-003 pattern for type safety
    const { params, body } = req as unknown as z.infer<typeof updateUserRoleSchema>;
    try {
      const updatedUser = await db.adminRepo.updateUserRole(params.id, body.role, req.log);
      sendSuccess(res, updatedUser);
    } catch (error) {
      req.log.error({ error }, `Error updating user ${params.id}:`);
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/users/{id}:
 *   delete:
 *     tags: [Admin]
 *     summary: Delete a user
 *     description: Delete a user account. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *           format: uuid
 *         description: User ID
 *     responses:
 *       204:
 *         description: User deleted successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: User not found
 */
router.delete(
  '/users/:id',
  validateRequest(uuidParamSchema('id', 'A valid user ID is required.')),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    // Apply ADR-003 pattern for type safety
    const { params } = req as unknown as z.infer<ReturnType<typeof uuidParamSchema>>;
    try {
      await userService.deleteUserAsAdmin(userProfile.user.user_id, params.id, req.log);
      sendNoContent(res);
    } catch (error) {
      req.log.error({ error }, 'Error deleting user');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/trigger/daily-deal-check:
 *   post:
 *     tags: [Admin]
 *     summary: Trigger daily deal check
 *     description: Manually trigger the daily deal check job. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       202:
 *         description: Job triggered successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/trigger/daily-deal-check',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    req.log.info(
      `[Admin] Manual trigger for daily deal check received from user: ${userProfile.user.user_id}`,
    );

    try {
      // We call the function but don't wait for it to finish (no `await`).
      // This is a "fire-and-forget" operation from the client's perspective.
      backgroundJobService.runDailyDealCheck();
      sendSuccess(
        res,
        {
          message:
            'Daily deal check job has been triggered successfully. It will run in the background.',
        },
        202,
      );
    } catch (error) {
      req.log.error({ error }, '[Admin] Failed to trigger daily deal check job.');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/trigger/analytics-report:
 *   post:
 *     tags: [Admin]
 *     summary: Trigger analytics report
 *     description: Manually enqueue a job to generate the daily analytics report. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       202:
 *         description: Job enqueued successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/trigger/analytics-report',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    req.log.info(
      `[Admin] Manual trigger for analytics report generation received from user: ${userProfile.user.user_id}`,
    );

    try {
      const jobId = await backgroundJobService.triggerAnalyticsReport();
      sendSuccess(
        res,
        {
          message: `Analytics report generation job has been enqueued successfully. Job ID: ${jobId}`,
        },
        202,
      );
    } catch (error) {
      req.log.error({ error }, '[Admin] Failed to enqueue analytics report job.');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/flyers/{flyerId}/cleanup:
 *   post:
 *     tags: [Admin]
 *     summary: Trigger flyer file cleanup
 *     description: Enqueue a job to clean up a flyer's files. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: flyerId
 *         required: true
 *         schema:
 *           type: integer
 *         description: Flyer ID
 *     responses:
 *       202:
 *         description: Cleanup job enqueued successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Flyer not found
 */
router.post(
  '/flyers/:flyerId/cleanup',
  adminTriggerLimiter,
  validateRequest(numericIdParam('flyerId')),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    // Infer type from the schema generator for type safety, as per ADR-003.
    const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParam>>; // This was a duplicate, fixed.
    req.log.info(
      `[Admin] Manual trigger for flyer file cleanup received from user: ${userProfile.user.user_id} for flyer ID: ${params.flyerId}`,
    );

    // Enqueue the cleanup job. The worker will handle the file deletion.
    try {
      await cleanupQueue.add('cleanup-flyer-files', { flyerId: params.flyerId });
      sendSuccess(
        res,
        { message: `File cleanup job for flyer ID ${params.flyerId} has been enqueued.` },
        202,
      );
    } catch (error) {
      req.log.error({ error }, 'Error enqueuing cleanup job');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/trigger/failing-job:
 *   post:
 *     tags: [Admin]
 *     summary: Trigger failing test job
 *     description: Enqueue a test job designed to fail for testing retry mechanisms. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       202:
 *         description: Failing test job enqueued successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/trigger/failing-job',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    req.log.info(
      `[Admin] Manual trigger for a failing job received from user: ${userProfile.user.user_id}`,
    );

    try {
      // Add a job with a special 'forceFail' flag that the worker will recognize.
      const job = await analyticsQueue.add('generate-daily-report', { reportDate: 'FAIL' });
      sendSuccess(
        res,
        { message: `Failing test job has been enqueued successfully. Job ID: ${job.id}` },
        202,
      );
    } catch (error) {
      req.log.error({ error }, 'Error enqueuing failing job');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/system/clear-geocode-cache:
 *   post:
 *     tags: [Admin]
 *     summary: Clear geocode cache
 *     description: Clears the Redis cache for geocoded addresses. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Cache cleared successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/system/clear-geocode-cache',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    req.log.info(
      `[Admin] Manual trigger for geocode cache clear received from user: ${userProfile.user.user_id}`,
    );

    try {
      const keysDeleted = await geocodingService.clearGeocodeCache(req.log);
      sendSuccess(res, {
        message: `Successfully cleared the geocode cache. ${keysDeleted} keys were removed.`,
      });
    } catch (error) {
      req.log.error({ error }, '[Admin] Failed to clear geocode cache.');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/workers/status:
 *   get:
 *     tags: [Admin]
 *     summary: Get worker statuses
 *     description: Get the current running status of all BullMQ workers. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Worker status information
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get(
  '/workers/status',
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const workerStatuses = await monitoringService.getWorkerStatuses();
      sendSuccess(res, workerStatuses);
    } catch (error) {
      req.log.error({ error }, 'Error fetching worker statuses');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/queues/status:
 *   get:
 *     tags: [Admin]
 *     summary: Get queue statuses
 *     description: Get job counts for all BullMQ queues. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Queue status information
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.get(
  '/queues/status',
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const queueStatuses = await monitoringService.getQueueStatuses();
      sendSuccess(res, queueStatuses);
    } catch (error) {
      req.log.error({ error }, 'Error fetching queue statuses');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/jobs/{queueName}/{jobId}/retry:
 *   post:
 *     tags: [Admin]
 *     summary: Retry a failed job
 *     description: Retries a specific failed job in a queue. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: queueName
 *         required: true
 *         schema:
 *           type: string
 *           enum: [flyer-processing, email-sending, analytics-reporting, file-cleanup, weekly-analytics-reporting]
 *         description: Queue name
 *       - in: path
 *         name: jobId
 *         required: true
 *         schema:
 *           type: string
 *         description: Job ID
 *     responses:
 *       200:
 *         description: Job marked for retry successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 *       404:
 *         description: Job not found
 */
router.post(
  '/jobs/:queueName/:jobId/retry',
  adminTriggerLimiter,
  validateRequest(jobRetrySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    const {
      params: { queueName, jobId },
    } = req as unknown as z.infer<typeof jobRetrySchema>;

    try {
      await monitoringService.retryFailedJob(queueName, jobId, userProfile.user.user_id);
      sendSuccess(res, { message: `Job ${jobId} has been successfully marked for retry.` });
    } catch (error) {
      req.log.error({ error }, 'Error retrying job');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/trigger/weekly-analytics:
 *   post:
 *     tags: [Admin]
 *     summary: Trigger weekly analytics
 *     description: Manually trigger the weekly analytics report job. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       202:
 *         description: Job enqueued successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/trigger/weekly-analytics',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile; // This was a duplicate, fixed.
    req.log.info(
      `[Admin] Manual trigger for weekly analytics report received from user: ${userProfile.user.user_id}`,
    );

    try {
      const jobId = await backgroundJobService.triggerWeeklyAnalyticsReport();
      sendSuccess(res, { message: 'Successfully enqueued weekly analytics job.', jobId }, 202);
    } catch (error) {
      req.log.error({ error }, 'Error enqueuing weekly analytics job');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/trigger/token-cleanup:
 *   post:
 *     tags: [Admin]
 *     summary: Trigger token cleanup
 *     description: Manually trigger the expired token cleanup job. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       202:
 *         description: Job enqueued successfully
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/trigger/token-cleanup',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    req.log.info(
      `[Admin] Manual trigger for token cleanup received from user: ${userProfile.user.user_id}`,
    );

    try {
      const jobId = await backgroundJobService.triggerTokenCleanup();
      sendSuccess(res, { message: 'Successfully enqueued token cleanup job.', jobId }, 202);
    } catch (error) {
      req.log.error({ error }, 'Error enqueuing token cleanup job');
      next(error);
    }
  },
);

/**
 * @openapi
 * /admin/system/clear-cache:
 *   post:
 *     tags: [Admin]
 *     summary: Clear application cache
 *     description: Clears cached flyers, brands, and stats data from Redis. Requires admin role.
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Cache cleared successfully with details
 *       401:
 *         description: Unauthorized
 *       403:
 *         description: Forbidden - admin role required
 */
router.post(
  '/system/clear-cache',
  adminTriggerLimiter,
  validateRequest(emptySchema),
  async (req: Request, res: Response, next: NextFunction) => {
    const userProfile = req.user as UserProfile;
    req.log.info(`[Admin] Manual cache clear received from user: ${userProfile.user.user_id}`);

    try {
      const [flyersDeleted, brandsDeleted, statsDeleted] = await Promise.all([
        cacheService.invalidateFlyers(req.log),
        cacheService.invalidateBrands(req.log),
        cacheService.invalidateStats(req.log),
      ]);

      const totalDeleted = flyersDeleted + brandsDeleted + statsDeleted;
      sendSuccess(res, {
        message: `Successfully cleared the application cache. ${totalDeleted} keys were removed.`,
        details: {
          flyers: flyersDeleted,
          brands: brandsDeleted,
          stats: statsDeleted,
        },
      });
    } catch (error) {
      req.log.error({ error }, '[Admin] Failed to clear application cache.');
      next(error);
    }
  },
);

/* Catches errors from multer (e.g., file size, file filter) */
router.use(handleMulterError);

export default router;
