// src/routes/gamification.routes.ts
import express, { NextFunction } from 'express';
import { z } from 'zod';
import passport, { isAdmin } from './passport.routes';
import { gamificationRepo } from '../services/db/index.db';
import { logger } from '../services/logger.server';
import { UserProfile } from '../types';
import { ForeignKeyConstraintError } from '../services/db/errors.db';
import { validateRequest } from '../middleware/validation.middleware';

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

// Helper for consistent required string validation (handles missing/null/empty)
const requiredString = (message: string) => z.preprocess((val) => val ?? '', z.string().min(1, message));

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

const leaderboardSchema = z.object({
  query: z.object({
    limit: z.coerce.number().int().positive().max(50).optional().default(10),
  }),
});

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

// --- Public Routes ---

/**
 * GET /api/achievements - Get the master list of all available achievements.
 * This is a public endpoint.
 */
router.get('/', async (req, res, next: NextFunction) => {
  try {
    const achievements = await gamificationRepo.getAllAchievements(req.log);
    res.json(achievements);
  } catch (error) {
    logger.error({ error }, 'Error fetching all achievements in /api/achievements:');
    next(error);
  }
});

/**
 * GET /api/achievements/leaderboard - Get the top users by points.
 * This is a public endpoint.
 */
router.get('/leaderboard', validateRequest(leaderboardSchema), async (req, res, next: NextFunction): Promise<void> => {
  // Apply ADR-003 pattern for type safety.
  // Explicitly coerce query params to ensure numbers are passed to the repo,
  // as validateRequest might not replace req.query in all test environments.
  const query = req.query as unknown as { limit?: string };
  const limit = query.limit ? Number(query.limit) : 10;

  try {
    const leaderboard = await gamificationRepo.getLeaderboard(limit, req.log);
    res.json(leaderboard);
  } catch (error) {
    logger.error({ error }, 'Error fetching leaderboard:');
    next(error);
  }
});

// --- Authenticated User Routes ---

/**
 * GET /api/achievements/me - Get all achievements for the authenticated user.
 * This is a protected endpoint.
 */
router.get(
  '/me',
  passport.authenticate('jwt', { session: false }),
  async (req, res, next: NextFunction): Promise<void> => {
    const user = req.user as UserProfile;
    try {
      const userAchievements = await gamificationRepo.getUserAchievements(user.user_id, req.log);
      res.json(userAchievements);
    } catch (error) {
      logger.error({ error, userId: 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);

/**
 * POST /api/achievements/award - Manually award an achievement to a user.
 * This is an admin-only endpoint.
 */
adminGamificationRouter.post(
  '/award', 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 gamificationRepo.awardAchievement(body.userId, body.achievementName, req.log);
      res.status(200).json({ message: `Successfully awarded '${body.achievementName}' to user ${body.userId}.` });
    } catch (error) {
      if (error instanceof ForeignKeyConstraintError) {
        res.status(400).json({ message: error.message });
        return;
      }
      logger.error({ error, userId: body.userId, achievementName: body.achievementName }, 'Error awarding achievement via admin endpoint:');
      next(error);
    }
  }
);

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

export default router;