/**
 * Admin Routes
 *
 * Route definitions for admin-only endpoints
 */

import { Router, Response, Request, NextFunction } from 'express';
import { z } from 'zod';
import jwt from 'jsonwebtoken';
import { asyncHandler } from '../../shared/middleware/validation.middleware';
import { requireAdmin } from '../../shared/middleware/auth.middleware';
import { aiExerciseGenerator } from '../exercise/generators/ai-exercise.generator';
import { ExerciseType, ExerciseDifficulty, TopicType, ModuleType } from '@prisma/client';
import { logger } from '../../shared/utils/logger';
import { prisma } from '../../shared/database/prisma.client';
import { JwtPayload } from '../../shared/types';
import { 
  UpdateExerciseSchema, 
  UpdateModuleSchema,
  PublishModuleSchema,
  PublishExerciseSchema,
  RegenerateExerciseSchema,
  validateBody 
} from './dtos/admin.dto';

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

const generateExerciseSchema = z.object({
  topic: z.nativeEnum(TopicType),
  moduleType: z.nativeEnum(ModuleType),
  exerciseType: z.nativeEnum(ExerciseType),
  difficulty: z.nativeEnum(ExerciseDifficulty),
  count: z.number().min(1).max(20).optional().default(1),
  moduleId: z.string().optional(),
  topicId: z.string().optional(),
  isPublished: z.boolean().optional().default(false),
  customPoints: z.number().min(1).max(100).optional(),
  timeLimitSeconds: z.number().min(30).max(3600).optional(),
  context: z.string().optional(),
});

// ============================================
// SSE AUTHENTICATION MIDDLEWARE
// ============================================

/**
 * Middleware for SSE endpoints that supports token via query parameter
 * This is needed because EventSource doesn't support custom headers
 */
async function requireAdminSSE(req: Request, res: Response, next: NextFunction): Promise<void> {
  try {
    // Try to get token from Authorization header first
    let token: string | null = null;
    const authHeader = req.headers.authorization;

    if (authHeader) {
      const parts = authHeader.split(' ');
      if (parts.length === 2 && parts[0] === 'Bearer' && parts[1]) {
        token = parts[1];
      }
    }

    // Fallback to query parameter for SSE
    if (!token && req.query.token) {
      token = req.query.token as string;
    }

    if (!token) {
      res.status(401).json({
        success: false,
        error: {
          code: 'AUTHENTICATION_ERROR',
          message: 'No token provided',
        },
      });
      return;
    }

    // Verify token
    const secret = process.env.JWT_SECRET;
    if (!secret) {
      throw new Error('JWT_SECRET not configured');
    }

    const decoded = jwt.verify(token, secret) as JwtPayload;
    req.user = decoded;

    // Check if user is admin
    const adminEmails = process.env.ADMIN_EMAILS?.split(',').map(e => e.trim()) || [];

    // Check by email from token
    if (decoded.email && adminEmails.includes(decoded.email)) {
      return next();
    }

    // Check by role from database
    const user = await prisma.user.findUnique({
      where: { id: decoded.userId },
      select: { role: true, email: true },
    });

    if (!user) {
      res.status(401).json({
        success: false,
        error: {
          code: 'AUTHENTICATION_ERROR',
          message: 'User not found',
        },
      });
      return;
    }

    // Check if admin
    if (user.role === 'ADMIN' || adminEmails.includes(user.email)) {
      return next();
    }

    res.status(403).json({
      success: false,
      error: {
        code: 'AUTHORIZATION_ERROR',
        message: 'Admin access required',
      },
    });
  } catch (error) {
    logger.error({ error }, 'SSE authentication failed');
    res.status(401).json({
      success: false,
      error: {
        code: 'AUTHENTICATION_ERROR',
        message: 'Invalid or expired token',
      },
    });
  }
}

// ============================================
// SSE PROGRESS EVENTS
// ============================================

export interface SSEProgressEvent {
  step: 'validating' | 'building_prompt' | 'analyzing' | 'generating' | 'validating_output' | 'saving' | 'saving_progress' | 'complete' | 'error';
  message: string;
  progress: number;
  data?: Record<string, unknown>;
}

/**
 * Send SSE progress event
 */
function sendSSEProgress(res: Response, event: SSEProgressEvent): void {
  res.write(`event: progress\n`);
  res.write(`data: ${JSON.stringify(event)}\n\n`);
}

/**
 * Send SSE complete event
 */
function sendSSEComplete(res: Response, data: Record<string, unknown>): void {
  res.write(`event: complete\n`);
  res.write(`data: ${JSON.stringify(data)}\n\n`);
}

/**
 * Send SSE error event
 */
function sendSSEError(res: Response, error: string): void {
  res.write(`event: error\n`);
  res.write(`data: ${JSON.stringify({ error })}\n\n`);
}

// ============================================
// ROUTES FACTORY
// ============================================

export function createAdminRoutes(): Router {
  const router = Router();

  // SSE endpoint with special authentication (supports query param token)
  // Must be placed before the general requireAdmin middleware
  router.get(
    '/exercises/generate/stream',
    requireAdminSSE,
    asyncHandler(async (req, res) => {
      // Parse query parameters
      const queryParams = {
        topic: req.query.topic as TopicType,
        moduleType: req.query.moduleType as ModuleType,
        exerciseType: req.query.exerciseType as ExerciseType,
        difficulty: req.query.difficulty as ExerciseDifficulty,
        count: parseInt(req.query.count as string || '1', 10),
        moduleId: req.query.moduleId as string | undefined,
        topicId: req.query.topicId as string | undefined,
        isPublished: req.query.isPublished === 'true',
        customPoints: req.query.customPoints ? parseInt(req.query.customPoints as string, 10) : undefined,
        timeLimitSeconds: req.query.timeLimitSeconds ? parseInt(req.query.timeLimitSeconds as string, 10) : undefined,
        context: req.query.context as string | undefined,
      };

      // Validate parameters
      const validation = generateExerciseSchema.safeParse(queryParams);

      if (!validation.success) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Invalid query parameters',
            details: validation.error.errors,
          },
        });
        return;
      }

      // Setup SSE headers
      res.setHeader('Content-Type', 'text/event-stream');
      res.setHeader('Cache-Control', 'no-cache');
      res.setHeader('Connection', 'keep-alive');
      res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
      res.flushHeaders();

      const data = validation.data;
      const count = data.count || 1;

      // Progress callback function
      const onProgress = (event: SSEProgressEvent) => {
        try {
          sendSSEProgress(res, event);
        } catch (err) {
          logger.error({ err }, 'Failed to send SSE progress event');
        }
      };

      try {
        // Send initial progress
        sendSSEProgress(res, {
          step: 'validating',
          message: 'Validando parámetros...',
          progress: 5,
        });

        // Small delay for UX
        await new Promise(resolve => setTimeout(resolve, 100));

        const result = count === 1
          ? await aiExerciseGenerator.generateExerciseWithProgress(
              {
                topic: data.topic,
                moduleType: data.moduleType,
                exerciseType: data.exerciseType,
                difficulty: data.difficulty,
                context: data.context,
              },
              {
                moduleId: data.moduleId,
                topicId: data.topicId,
                isPublished: data.isPublished,
                customPoints: data.customPoints,
                timeLimitSeconds: data.timeLimitSeconds,
                saveToDatabase: true,
              },
              onProgress
            )
          : await aiExerciseGenerator.generateExercisesWithProgress(
              {
                topic: data.topic,
                moduleType: data.moduleType,
                exerciseType: data.exerciseType,
                difficulty: data.difficulty,
                count,
                context: data.context,
              },
              {
                moduleId: data.moduleId,
                topicId: data.topicId,
                isPublished: data.isPublished,
                customPoints: data.customPoints,
                timeLimitSeconds: data.timeLimitSeconds,
                saveToDatabase: true,
              },
              onProgress
            );

        // Send completion event
        sendSSEComplete(res, {
          success: result.success,
          exercisesGenerated: result.exercisesGenerated,
          exercisesSaved: result.exercisesSaved,
          exerciseIds: result.exerciseIds,
          errors: result.errors,
          metadata: result.metadata,
        });

      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        logger.error({ error }, 'SSE exercise generation failed');
        sendSSEError(res, errorMessage);
      } finally {
        res.end();
      }
    })
  );

  // All other admin routes require admin authentication (via header)
  router.use(requireAdmin);

  /**
   * GET /api/admin/exercises/generate/stream
   * SSE endpoint moved above - uses requireAdminSSE middleware
   */

  /**
   * POST /api/admin/exercises/generate
   * Generate exercises using AI
   *
   * Request body:
   * - topic: TopicType (e.g., VECTORES, DERIVADAS)
   * - moduleType: ModuleType (e.g., VECTORES, CALCULO_DIFERENCIAL)
   * - exerciseType: ExerciseType (e.g., MULTIPLE_CHOICE, CALCULATION)
   * - difficulty: ExerciseDifficulty (BASIC, INTERMEDIATE, ADVANCED, EXPERT)
   * - count: number (1-20, default 1)
   * - moduleId: string (optional, to associate with existing module)
   * - topicId: string (optional)
   * - isPublished: boolean (default false)
   * - customPoints: number (optional)
   * - timeLimitSeconds: number (optional)
   * - context: string (optional additional context for generation)
   *
   * Response:
   * - success: boolean
   * - exercisesGenerated: number
   * - exercisesSaved: number
   * - exerciseIds: string[]
   * - errors: string[]
   * - metadata: { topic, difficulty, modelUsed, generationTimeMs }
   */
  router.post(
    '/exercises/generate',
    asyncHandler(async (req, res) => {
      const validation = generateExerciseSchema.safeParse(req.body);

      if (!validation.success) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Invalid request body',
            details: validation.error.errors,
          },
        });
        return;
      }

      const data = validation.data;
      const count = data.count || 1;

      const result = count === 1
        ? await aiExerciseGenerator.generateExercise(
            {
              topic: data.topic,
              moduleType: data.moduleType,
              exerciseType: data.exerciseType,
              difficulty: data.difficulty,
              context: data.context,
            },
            {
              moduleId: data.moduleId,
              topicId: data.topicId,
              isPublished: data.isPublished,
              customPoints: data.customPoints,
              timeLimitSeconds: data.timeLimitSeconds,
              saveToDatabase: true,
            }
          )
        : await aiExerciseGenerator.generateExercises(
            {
              topic: data.topic,
              moduleType: data.moduleType,
              exerciseType: data.exerciseType,
              difficulty: data.difficulty,
              count,
              context: data.context,
            },
            {
              moduleId: data.moduleId,
              topicId: data.topicId,
              isPublished: data.isPublished,
              customPoints: data.customPoints,
              timeLimitSeconds: data.timeLimitSeconds,
              saveToDatabase: true,
            }
          );

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

  /**
   * POST /api/admin/exercises/regenerate/:exerciseId
   * Regenerate a specific exercise with feedback
   */
  router.post(
    '/exercises/regenerate/:exerciseId',
    validateBody(RegenerateExerciseSchema),
    asyncHandler(async (req, res) => {
      const { exerciseId } = req.params;
      const { feedback } = req.body;

      if (!exerciseId) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Exercise ID is required',
          },
        });
        return;
      }

      const result = await aiExerciseGenerator.regenerateExercise(exerciseId, feedback);

      logger.info({
        userId: (req as any).user?.userId,
        exerciseId,
      }, 'Admin regenerated exercise');

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

  // ============================================
  // MODULE MANAGEMENT ROUTES
  // ============================================

  /**
   * GET /api/admin/modules
   * List all modules (including unpublished)
   */
  router.get(
    '/modules',
    asyncHandler(async (_req, res) => {
      const modules = await prisma.modules.findMany({
        orderBy: { order: 'asc' },
        include: {
          _count: {
            select: { exercises: true },
          },
        },
      });

      res.json({
        success: true,
        data: modules.map(m => ({
          ...m,
          totalExercises: m._count.exercises,
        })),
      });
    })
  );

  /**
   * POST /api/admin/modules
   * Create a new module
   */
  router.post(
    '/modules',
    asyncHandler(async (req, res) => {
      const { name, description, type, order, introduction } = req.body;

      if (!name || !type) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'name and type are required',
          },
        });
        return;
      }

      // Get max order for this type if not provided
      const maxOrder = order ?? await prisma.modules.aggregate({
        where: { type: type as ModuleType },
        _max: { order: true },
      }).then(r => (r._max.order ?? 0) + 1);

      const module = await prisma.modules.create({
        data: {
          id: crypto.randomUUID(),
          name,
          description: description || '',
          type: type as ModuleType,
          order: maxOrder,
          introduction,
        },
      });

      res.status(201).json({
        success: true,
        data: module,
      });
    })
  );

  /**
   * GET /api/admin/modules/:id
   * Get module by ID
   */
  router.get(
    '/modules/:id',
    asyncHandler(async (req, res) => {
      const { id } = req.params;
      
      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Module ID is required',
          },
        });
        return;
      }

      const module = await prisma.modules.findUnique({
        where: { id },
        include: {
          topics: true,
          _count: {
            select: { exercises: true },
          },
        },
      });

      if (!module) {
        res.status(404).json({
          success: false,
          error: {
            code: 'NOT_FOUND',
            message: 'Module not found',
          },
        });
        return;
      }

      res.json({
        success: true,
        data: {
          ...module,
          totalExercises: module._count.exercises,
        },
      });
    })
  );

  /**
   * PUT /api/admin/modules/:id
   * Update a module
   */
  router.put(
    '/modules/:id',
    validateBody(UpdateModuleSchema),
    asyncHandler(async (req, res) => {
      const { id } = req.params;
      const updateData = req.body;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Module ID is required',
          },
        });
        return;
      }

      const module = await prisma.modules.update({
        where: { id },
        data: updateData,
      });

      logger.info({
        userId: (req as any).user?.userId,
        moduleId: id,
        changes: Object.keys(updateData),
      }, 'Admin updated module');

      res.json({
        success: true,
        data: module,
      });
    })
  );

  /**
   * PATCH /api/admin/modules/:id/publish
   * Toggle module publish status
   */
  router.patch(
    '/modules/:id/publish',
    validateBody(PublishModuleSchema),
    asyncHandler(async (req, res) => {
      const { id } = req.params;
      const { isPublished } = req.body;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Module ID is required',
          },
        });
        return;
      }

      const module = await prisma.modules.update({
        where: { id },
        data: { isPublished },
      });

      logger.info({
        userId: (req as any).user?.userId,
        moduleId: id,
        isPublished,
      }, 'Admin toggled module publish status');

      res.json({
        success: true,
        data: module,
      });
    })
  );

  /**
   * DELETE /api/admin/modules/:id
   * Delete a module
   */
  router.delete(
    '/modules/:id',
    asyncHandler(async (req, res) => {
      const { id } = req.params;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Module ID is required',
          },
        });
        return;
      }

      await prisma.modules.delete({
        where: { id },
      });

      res.json({
        success: true,
        data: { message: 'Module deleted' },
      });
    })
  );

  // ============================================
  // EXERCISE MANAGEMENT ROUTES
  // ============================================

  /**
   * GET /api/admin/exercises
   * List all exercises (including unpublished)
   */
  router.get(
    '/exercises',
    asyncHandler(async (req, res) => {
      const { moduleId, difficulty, isPublished, page = 1, limit = 50 } = req.query;

      const where: any = {};
      if (moduleId) where.moduleId = moduleId as string;
      if (difficulty) where.difficulty = difficulty as ExerciseDifficulty;
      if (isPublished !== undefined) where.isPublished = isPublished === 'true';

      const exercises = await prisma.exercise.findMany({
        where,
        orderBy: [{ moduleId: 'asc' }, { order: 'asc' }],
        skip: (Number(page) - 1) * Number(limit),
        take: Number(limit),
        include: {
          modules: {
            select: { id: true, name: true },
          },
        },
      });

      const total = await prisma.exercise.count({ where });

      res.json({
        success: true,
        data: exercises,
        meta: {
          total,
          page: Number(page),
          limit: Number(limit),
          totalPages: Math.ceil(total / Number(limit)),
        },
      });
    })
  );

  /**
   * GET /api/admin/exercises/:id
   * Get exercise by ID
   */
  router.get(
    '/exercises/:id',
    asyncHandler(async (req, res) => {
      const { id } = req.params;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Exercise ID is required',
          },
        });
        return;
      }

      const exercise = await prisma.exercise.findUnique({
        where: { id },
        include: {
          modules: {
            select: { id: true, name: true },
          },
          topics: {
            select: { id: true, name: true },
          },
        },
      });

      if (!exercise) {
        res.status(404).json({
          success: false,
          error: {
            code: 'NOT_FOUND',
            message: 'Exercise not found',
          },
        });
        return;
      }

      res.json({
        success: true,
        data: exercise,
      });
    })
  );

  /**
   * PUT /api/admin/exercises/:id
   * Update an exercise
   */
  router.put(
    '/exercises/:id',
    validateBody(UpdateExerciseSchema),
    asyncHandler(async (req, res) => {
      const { id } = req.params;
      const updateData = req.body;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Exercise ID is required',
          },
        });
        return;
      }

      const exercise = await prisma.exercise.update({
        where: { id },
        data: updateData,
      });

      logger.info({
        userId: (req as any).user?.userId,
        exerciseId: id,
        changes: Object.keys(updateData),
      }, 'Admin updated exercise');

      res.json({
        success: true,
        data: exercise,
      });
    })
  );

  /**
   * PATCH /api/admin/exercises/:id/publish
   * Toggle exercise publish status
   */
  router.patch(
    '/exercises/:id/publish',
    validateBody(PublishExerciseSchema),
    asyncHandler(async (req, res) => {
      const { id } = req.params;
      const { isPublished } = req.body;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Exercise ID is required',
          },
        });
        return;
      }

      const exercise = await prisma.exercise.update({
        where: { id },
        data: { isPublished },
      });

      logger.info({
        userId: (req as any).user?.userId,
        exerciseId: id,
        isPublished,
      }, 'Admin toggled exercise publish status');

      res.json({
        success: true,
        data: exercise,
      });
    })
  );

  /**
   * DELETE /api/admin/exercises/:id
   * Delete an exercise
   */
  router.delete(
    '/exercises/:id',
    asyncHandler(async (req, res) => {
      const { id } = req.params;

      if (!id) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Exercise ID is required',
          },
        });
        return;
      }

      await prisma.exercise.delete({
        where: { id },
      });

      res.json({
        success: true,
        data: { message: 'Exercise deleted' },
      });
    })
  );

  // ============================================
  // STATISTICS ROUTES
  // ============================================

  /**
   * GET /api/admin/stats
   * Get admin statistics
   */
  router.get(
    '/stats',
    asyncHandler(async (_req, res) => {
      const [
        totalUsers,
        totalModules,
        totalExercises,
        publishedExercises,
        aiGeneratedExercises,
        totalAttempts,
        correctAttempts,
        recentUsers,
        exercisesByDifficulty,
      ] = await Promise.all([
        prisma.user.count(),
        prisma.modules.count(),
        prisma.exercise.count(),
        prisma.exercise.count({ where: { isPublished: true } }),
        prisma.exercise.count({ where: { isAIGenerated: true } }),
        prisma.exerciseAttempt.count(),
        prisma.exerciseAttempt.count({ where: { status: 'CORRECT' } }),
        prisma.user.count({
          where: {
            createdAt: {
              gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
            },
          },
        }),
        prisma.exercise.groupBy({
          by: ['difficulty'],
          _count: true,
        }),
      ]);

      const byDifficulty = {
        BASIC: 0,
        INTERMEDIATE: 0,
        ADVANCED: 0,
        EXPERT: 0,
      };

      exercisesByDifficulty.forEach(item => {
        byDifficulty[item.difficulty] = item._count;
      });

      res.json({
        success: true,
        data: {
          users: {
            total: totalUsers,
            newThisWeek: recentUsers,
          },
          modules: {
            total: totalModules,
          },
          exercises: {
            total: totalExercises,
            published: publishedExercises,
            aiGenerated: aiGeneratedExercises,
            byDifficulty,
          },
          attempts: {
            total: totalAttempts,
            correct: correctAttempts,
            correctRate: totalAttempts > 0 ? (correctAttempts / totalAttempts) * 100 : 0,
          },
        },
      });
    })
  );

  return router;
}

export default createAdminRoutes;