/**
 * Ranking Controller
 *
 * HTTP request handlers for ranking and badge endpoints.
 * All ranking data returned is anonymous (no personal information).
 */

import { Request, Response } from 'express';
import { z } from 'zod';
import { RankingService } from './ranking.service';
import { StreakCalculator } from './calculators/streak.calculator';
import { ValidationError, NotFoundError, AuthenticationError } from '../../shared/types';
import { asyncHandler } from '../../shared/middleware/validation.middleware';
import { prisma } from '../../shared/database/prisma.client';

// ============================================
// VALIDATION SCHEMAS
// ============================================

const getGlobalRankingSchema = z.object({
  limit: z.coerce.number().min(1).max(100).optional().default(100),
  offset: z.coerce.number().min(0).optional().default(0),
});

const getPeriodRankingSchema = z.object({
  period: z.enum(['daily', 'weekly', 'monthly', 'all-time']).optional().default('all-time'),
  limit: z.coerce.number().min(1).max(100).optional().default(100),
  offset: z.coerce.number().min(0).optional().default(0),
});

const getModuleRankingSchema = z.object({
  limit: z.coerce.number().min(1).max(100).optional().default(100),
  offset: z.coerce.number().min(0).optional().default(0),
});

const getRankingAroundUserSchema = z.object({
  radius: z.coerce.number().min(3).max(20).optional().default(5),
  moduleId: z.string().optional(),
});

// ============================================
// RANKING CONTROLLER
// ============================================

export class RankingController {
  /**
   * GET /api/ranking/global
   * Get global ranking (anonymous)
   */
  getGlobalRanking = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const result = getGlobalRankingSchema.safeParse(req.query);

    if (!result.success) {
      throw new ValidationError('Invalid query parameters', result.error.errors);
    }

    const { limit, offset } = result.data;
    const { rankings, total } = await RankingService.getGlobalRanking({ limit, offset });

    res.json({
      success: true,
      data: {
        rankings,
        pagination: {
          limit,
          offset,
          total,
          hasMore: offset + limit < total,
        },
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/period
   * Get period-based ranking (daily, weekly, monthly, all-time)
   */
  getPeriodRanking = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const result = getPeriodRankingSchema.safeParse(req.query);

    if (!result.success) {
      throw new ValidationError('Invalid query parameters', result.error.errors);
    }

    const { period, limit, offset } = result.data;
    const { rankings, total, period: returnedPeriod } = await RankingService.getPeriodRanking(period, { limit, offset });

    res.json({
      success: true,
      data: {
        rankings,
        period: returnedPeriod,
        pagination: {
          limit,
          offset,
          total,
          hasMore: offset + limit < total,
        },
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/module/:moduleId
   * Get module ranking (anonymous)
   */
  getModuleRanking = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const { moduleId } = req.params;
    
    if (!moduleId) {
      throw new ValidationError('Module ID is required');
    }
    
    const result = getModuleRankingSchema.safeParse(req.query);

    if (!result.success) {
      throw new ValidationError('Invalid query parameters', result.error.errors);
    }

    const { limit, offset } = result.data;
    const { rankings, total } = await RankingService.getModuleRanking(moduleId, { limit, offset });

    res.json({
      success: true,
      data: {
        moduleId,
        rankings,
        pagination: {
          limit,
          offset,
          total,
          hasMore: offset + limit < total,
        },
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/my-position
   * Get authenticated user's ranking position (anonymous data only)
   */
  getMyPosition = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const userId = req.user.userId;
    const rankingInfo = await RankingService.getUserRankingInfo(userId);

    res.json({
      success: true,
      data: rankingInfo,
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/around-me
   * Get ranking around authenticated user's position
   */
  getRankingAroundMe = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const result = getRankingAroundUserSchema.safeParse(req.query);

    if (!result.success) {
      throw new ValidationError('Invalid query parameters', result.error.errors);
    }

    const { radius, moduleId } = result.data;
    const userId = req.user.userId;

    const rankings = await RankingService.getRankingAroundUser(userId, moduleId, radius);

    res.json({
      success: true,
      data: {
        rankings,
        radius,
        moduleId: moduleId || null,
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/achievements
   * Get all available badges/achievements
   */
  getAllAchievements = asyncHandler(async (_req: Request, res: Response): Promise<void> => {
    const badges = await RankingService.getAllBadges();

    res.json({
      success: true,
      data: badges,
      meta: {
        timestamp: new Date().toISOString(),
        total: badges.length,
      },
    });
  });

  /**
   * GET /api/achievements/my
   * Get authenticated user's badges with progress
   */
  getMyAchievements = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const userId = req.user.userId;
    const badges = await RankingService.getUserBadges(userId);

    // Separate unlocked and locked
    const unlocked = badges.filter((b) => b.unlocked);
    const locked = badges.filter((b) => !b.unlocked);

    res.json({
      success: true,
      data: {
        unlocked,
        locked,
        summary: {
          total: badges.length,
          unlocked: unlocked.length,
          locked: locked.length,
          completionPercentage: Math.round((unlocked.length / badges.length) * 100),
        },
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/achievements/my/unlocked
   * Get only unlocked badges
   */
  getUnlockedAchievements = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const userId = req.user.userId;
    const badges = await RankingService.getUnlockedBadges(userId);

    res.json({
      success: true,
      data: badges,
      meta: {
        timestamp: new Date().toISOString(),
        total: badges.length,
      },
    });
  });

  /**
   * GET /api/achievements/:badgeCode
   * Get progress for a specific badge
   */
  getBadgeProgress = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const { badgeCode } = req.params;
    
    if (!badgeCode) {
      throw new ValidationError('Badge code is required');
    }
    
    const userId = req.user.userId;

    const progress = await RankingService.getBadgeProgress(userId, badgeCode);

    if (!progress) {
      throw new NotFoundError('Badge');
    }

    res.json({
      success: true,
      data: progress,
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/achievements/summary
   * Get user's achievement summary
   */
  getAchievementSummary = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const userId = req.user.userId;
    const summary = await RankingService.getUserAchievementSummary(userId);

    res.json({
      success: true,
      data: summary,
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/statistics
   * Get global ranking statistics (public endpoint)
   */
  getRankingStatistics = asyncHandler(async (_req: Request, res: Response): Promise<void> => {
    const stats = await RankingService.getRankingStatistics();

    res.json({
      success: true,
      data: stats,
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/modules
   * Get list of modules with ranking availability
   */
  getModulesWithRanking = asyncHandler(async (_req: Request, res: Response): Promise<void> => {
    const modules = await prisma.modules.findMany({
      where: {
        isPublished: true,
      },
      select: {
        id: true,
        name: true,
        type: true,
        order: true,
        _count: {
          select: {
            rankings: true,
          },
        },
      },
      orderBy: {
        order: 'asc',
      },
    });

    res.json({
      success: true,
      data: modules.map((m) => ({
        id: m.id,
        name: m.name,
        type: m.type,
        order: m.order,
        participants: m._count.rankings,
      })),
      meta: {
        timestamp: new Date().toISOString(),
        total: modules.length,
      },
    });
  });

  /**
   * POST /api/ranking/recalculate (Admin only)
   * Recalculate all rankings
   */
  recalculateRankings = asyncHandler(async (_req: Request, res: Response): Promise<void> => {
    // This would require admin middleware in production
    // For now, we'll just trigger the recalculation
    await RankingService.recalculateAllRankings();

    res.json({
      success: true,
      data: {
        message: 'Rankings recalculated successfully',
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * POST /api/achievements/initialize (Admin only)
   * Initialize all badges in the database
   */
  initializeBadges = asyncHandler(async (_req: Request, res: Response): Promise<void> => {
    await RankingService.initializeBadges();

    res.json({
      success: true,
      data: {
        message: 'Badges initialized successfully',
      },
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });

  /**
   * GET /api/ranking/streak
   * Get authenticated user's current streak information
   */
  getUserStreak = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const userId = req.user.userId;
    
    // Get user's timezone from database or request
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: { timezone: true },
    });

    const timezone = user?.timezone || 'UTC';
    const streak = await StreakCalculator.calculateStreak({ userId, timezone });

    res.json({
      success: true,
      data: streak,
      meta: {
        timestamp: new Date().toISOString(),
        timezone,
      },
    });
  });

  /**
   * POST /api/ranking/update-me
   * Force update authenticated user's ranking
   */
  updateMyRanking = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    if (!req.user) {
      throw new AuthenticationError('Authentication required');
    }

    const userId = req.user.userId;
    await RankingService.updateUserRanking(userId);

    const rankingInfo = await RankingService.getUserRankingInfo(userId);

    res.json({
      success: true,
      data: rankingInfo,
      meta: {
        timestamp: new Date().toISOString(),
      },
    });
  });
}

// Export singleton instance
const rankingController = new RankingController();
export { rankingController };
export default rankingController;