// src/routes/gamification.routes.ts
import express, { NextFunction } from 'express';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { z } from 'zod';
import passport, { isAdmin } from '../config/passport';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { gamificationService } from '../services/gamificationService';
// Removed: import { logger } from '../services/logger.server';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { UserProfile } from '../types';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { validateRequest } from '../middleware/validation.middleware';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { requiredString, optionalNumeric } from '../utils/zodUtils';
// All route handlers now use req.log (request-scoped logger) as per ADR-004
import { publicReadLimiter, userReadLimiter, adminTriggerLimiter } from '../config/rateLimiters';
import { sendSuccess } from '../utils/apiResponse';

const router = express.Router();
const adminGamificationRouter = express.Router(); // Create a new router for admin-only routes.

// --- Zod Schemas for Gamification Routes (as per ADR-003) ---

const leaderboardQuerySchema = z.object({
  limit: optionalNumeric({ default: 10, integer: true, positive: true, max: 50 }),
});

const leaderboardSchema = z.object({
  query: leaderboardQuerySchema,
});

const awardAchievementSchema = z.object({
  body: z.object({
    userId: requiredString('userId is required.'),
    achievementName: requiredString('achievementName is required.'),
  }),
});

// --- Public Routes ---

/**
 * @openapi
 * /achievements:
 *   get:
 *     summary: Get all achievements
 *     description: Returns the master list of all available achievements in the system. This is a public endpoint.
 *     tags:
 *       - Achievements
 *     responses:
 *       200:
 *         description: List of all achievements
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/Achievement'
 */
router.get('/', publicReadLimiter, async (req, res, next: NextFunction) => {
  try {
    const achievements = await gamificationService.getAllAchievements(req.log);
    sendSuccess(res, achievements);
  } catch (error) {
    req.log.error({ error }, 'Error fetching all achievements in /api/achievements:');
    next(error);
  }
});

/**
 * @openapi
 * /achievements/leaderboard:
 *   get:
 *     summary: Get leaderboard
 *     description: Returns the top users ranked by total points earned from achievements. This is a public endpoint.
 *     tags:
 *       - Achievements
 *     parameters:
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           minimum: 1
 *           maximum: 50
 *           default: 10
 *         description: Maximum number of users to return
 *     responses:
 *       200:
 *         description: Leaderboard entries
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/LeaderboardUser'
 */
router.get(
  '/leaderboard',
  publicReadLimiter,
  validateRequest(leaderboardSchema),
  async (req, res, next: NextFunction): Promise<void> => {
    try {
      // The `validateRequest` middleware ensures `req.query` is valid.
      // We parse it here to apply Zod's coercions (string to number) and defaults.
      const { limit } = leaderboardQuerySchema.parse(req.query);
      const leaderboard = await gamificationService.getLeaderboard(limit!, req.log);
      sendSuccess(res, leaderboard);
    } catch (error) {
      req.log.error({ error }, 'Error fetching leaderboard:');
      next(error);
    }
  },
);

// --- Authenticated User Routes ---

/**
 * @openapi
 * /achievements/me:
 *   get:
 *     summary: Get my achievements
 *     description: Returns all achievements earned by the authenticated user.
 *     tags:
 *       - Achievements
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of user's earned achievements
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/UserAchievement'
 *       401:
 *         description: Unauthorized - JWT token missing or invalid
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ErrorResponse'
 */
router.get(
  '/me',
  passport.authenticate('jwt', { session: false }),
  userReadLimiter,
  async (req, res, next: NextFunction): Promise<void> => {
    const userProfile = req.user as UserProfile;
    try {
      const userAchievements = await gamificationService.getUserAchievements(
        userProfile.user.user_id,
        req.log,
      );
      sendSuccess(res, userAchievements);
    } catch (error) {
      req.log.error(
        { error, userId: userProfile.user.user_id },
        'Error fetching user achievements:',
      );
      next(error);
    }
  },
);

// --- Admin-Only Routes ---

// Apply authentication and admin-check middleware to the entire admin sub-router.
adminGamificationRouter.use(passport.authenticate('jwt', { session: false }), isAdmin);

/**
 * @openapi
 * /achievements/award:
 *   post:
 *     summary: Award achievement to user (Admin only)
 *     description: Manually award an achievement to a specific user. Requires admin role.
 *     tags:
 *       - Achievements
 *       - Admin
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - userId
 *               - achievementName
 *             properties:
 *               userId:
 *                 type: string
 *                 format: uuid
 *                 description: The user ID to award the achievement to
 *               achievementName:
 *                 type: string
 *                 description: The name of the achievement to award
 *                 example: First-Upload
 *     responses:
 *       200:
 *         description: Achievement awarded successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 data:
 *                   type: object
 *                   properties:
 *                     message:
 *                       type: string
 *                       example: Successfully awarded 'First-Upload' to user abc123.
 *       401:
 *         description: Unauthorized - JWT token missing or invalid
 *       403:
 *         description: Forbidden - User is not an admin
 */
adminGamificationRouter.post(
  '/award',
  adminTriggerLimiter,
  validateRequest(awardAchievementSchema),
  async (req, res, next: NextFunction): Promise<void> => {
    // Infer type and cast request object as per ADR-003
    type AwardAchievementRequest = z.infer<typeof awardAchievementSchema>;
    const { body } = req as unknown as AwardAchievementRequest;
    try {
      await gamificationService.awardAchievement(body.userId, body.achievementName, req.log);
      sendSuccess(res, {
        message: `Successfully awarded '${body.achievementName}' to user ${body.userId}.`,
      });
    } catch (error) {
      next(error);
    }
  },
);

// Mount the admin sub-router onto the main gamification router.
router.use(adminGamificationRouter);

export default router;
