import { Response } from 'express';
import { z } from 'zod';
import { prisma } from '../index';
import { AuthenticatedRequest } from '../middlewares/auth';
import { createError, asyncHandler } from '../middlewares/errorHandler';
import logger from '../utils/logger';
import { NotificationService } from '../services/notificationService';

// Validation schemas
const uuidParamSchema = z.object({
  id: z.string().uuid('Invalid assignment ID'),
});

const createAssignmentSchema = z.object({
  studentId: z.string().uuid('Invalid student ID'),
  botId: z.string().uuid('Invalid bot ID'),
  notes: z.string().optional(),
});

const paginationSchema = z.object({
  page: z.string().transform(Number).pipe(z.number().min(1)).optional(),
  limit: z.string().transform(Number).pipe(z.number().min(1).max(100)).optional(),
  studentId: z.string().uuid('Invalid student ID').optional(),
  botId: z.string().uuid('Invalid bot ID').optional(),
});

type UuidParamInput = z.infer<typeof uuidParamSchema>;
type CreateAssignmentInput = z.infer<typeof createAssignmentSchema>;
type PaginationInput = z.infer<typeof paginationSchema>;

/**
 * Get all bot assignments with pagination and filtering
 */
export const getAllBotAssignments = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const validatedParams: PaginationInput = paginationSchema.parse(req.query);
  const { page = 1, limit = 20, studentId, botId } = validatedParams;

  if (!req.user) {
    throw createError('Authentication required', 401);
  }

  // Build where clause based on user role and filters
  const where: any = {};

  // Teachers can only see assignments for their assigned students
  if (req.user.role === 'TEACHER') {
    where.student = {
      assignedTeachers: { some: { teacherId: req.user.id } }
    };
  }

  if (studentId) {
    where.studentId = studentId;
    // If teacher, ensure the student is assigned to them
    if (req.user.role === 'TEACHER') {
      where.student = {
        ...where.student,
        id: studentId,
        assignedTeachers: { some: { teacherId: req.user.id } }
      };
      delete where.studentId; // Remove direct studentId filter as we're using nested filter
    }
  }

  if (botId) {
    where.botId = botId;
  }

  // Get assignments with related data
  const [assignments, total] = await Promise.all([
    prisma.studentBotAccess.findMany({
      where,
      include: {
        student: {
          select: {
            id: true,
            name: true,
            email: true,
            role: true,
          },
        },
        bot: {
          select: {
            id: true,
            name: true,
            topic: true,
            level: true,
            isActive: true,
          },
        },
        granter: {
          select: {
            id: true,
            name: true,
            email: true,
          },
        },
      },
      orderBy: {
        grantedAt: 'desc',
      },
      skip: (page - 1) * limit,
      take: limit,
    }),
    prisma.studentBotAccess.count({ where }),
  ]);

  const totalPages = Math.ceil(total / limit);

  logger.info('Bot assignments retrieved successfully', {
    userId: req.user.id,
    userRole: req.user.role,
    page,
    limit,
    total,
  });

  res.json({
    success: true,
    message: 'Bot assignments retrieved successfully',
    data: {
      assignments,
      pagination: {
        page,
        limit,
        total,
        pages: totalPages,
      },
    },
  });
});

/**
 * Create a new bot assignment
 */
export const createBotAssignment = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const validatedData: CreateAssignmentInput = createAssignmentSchema.parse(req.body);
  const { studentId, botId, notes } = validatedData;

  if (!req.user) {
    throw createError('Authentication required', 401);
  }

  // Check if student exists and is a student
  const student = await prisma.user.findUnique({
    where: { id: studentId },
    select: { id: true, role: true },
  });

  if (!student) {
    throw createError('Student not found', 404);
  }

  if (student.role !== 'STUDENT') {
    throw createError('Can only assign bots to students', 400);
  }

  // Check if bot exists and is active
  const bot = await prisma.bot.findUnique({
    where: { id: botId },
    select: { id: true, isActive: true },
  });

  if (!bot) {
    throw createError('Bot not found', 404);
  }

  if (!bot.isActive) {
    throw createError('Cannot assign inactive bot', 400);
  }

  // Check if assignment already exists
  const existingAssignment = await prisma.studentBotAccess.findUnique({
    where: {
      studentId_botId: {
        studentId,
        botId,
      },
    },
  });

  if (existingAssignment) {
    throw createError('Student already has access to this bot', 409);
  }

  // Create the assignment
  const assignment = await prisma.studentBotAccess.create({
    data: {
      studentId,
      botId,
      grantedBy: req.user.id,
      notes: notes || null,
    },
    include: {
      student: {
        select: {
          id: true,
          name: true,
          email: true,
          role: true,
        },
      },
      bot: {
        select: {
          id: true,
          name: true,
          topic: true,
          level: true,
          isActive: true,
        },
      },
      granter: {
        select: {
          id: true,
          name: true,
          email: true,
        },
      },
    },
  });

  // Create notification for the student
  try {
    await NotificationService.createBotAssignmentNotification(
      studentId,
      assignment.bot.name,
      assignment.granter.name
    );
    logger.info('Notification created for bot assignment', {
      studentId,
      botName: assignment.bot.name,
      grantedByName: assignment.granter.name,
    });
  } catch (notificationError) {
    logger.error('Failed to create notification for bot assignment', {
      error: notificationError instanceof Error ? notificationError.message : 'Unknown error',
      studentId,
      botId,
    });
    // Don't fail the assignment if notification creation fails
  }

  logger.info('Bot assignment created successfully', {
    assignmentId: assignment.id,
    studentId,
    botId,
    grantedBy: req.user.id,
  });

  res.status(201).json({
    success: true,
    message: 'Bot assignment created successfully',
    data: {
      assignment,
    },
  });
});

/**
 * Remove a bot assignment
 */
export const removeBotAssignment = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const { id }: UuidParamInput = uuidParamSchema.parse(req.params);

  if (!req.user) {
    throw createError('Authentication required', 401);
  }

  // Check if assignment exists
  const assignment = await prisma.studentBotAccess.findUnique({
    where: { id },
    include: {
      student: {
        select: {
          id: true,
          name: true,
          email: true,
        },
      },
      bot: {
        select: {
          id: true,
          name: true,
        },
      },
    },
  });

  if (!assignment) {
    throw createError('Assignment not found', 404);
  }

  // Delete the assignment
  await prisma.studentBotAccess.delete({
    where: { id },
  });

  logger.info('Bot assignment removed successfully', {
    assignmentId: id,
    studentId: assignment.studentId,
    botId: assignment.botId,
    removedBy: req.user.id,
  });

  res.json({
    success: true,
    message: 'Bot assignment removed successfully',
  });
});

/**
 * Get recently assigned but unused bots for a student
 */
export const getRecentlyAssignedUnusedBots = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  try {
    if (!req.user) {
      throw createError('Authentication required', 401);
    }

    if (req.user.role !== 'STUDENT') {
      throw createError('This endpoint is only available for students', 403);
    }

    logger.info('Getting recently assigned bots for student', { userId: req.user.id });

    // Get recently assigned bots with bot and granter information
    const recentlyAssignedBots = await prisma.studentBotAccess.findMany({
      where: {
        studentId: req.user.id,
      },
      include: {
        bot: {
          select: {
            id: true,
            name: true,
            topic: true,
            level: true,
            description: true,
            imageUrl: true,
            isActive: true,
          },
        },
        granter: {
          select: {
            id: true,
            name: true,
            email: true,
          },
        },
      },
      take: 10, // Limit to 10 results
      orderBy: {
        grantedAt: 'desc',
      },
    });

    logger.info('Found assignments', { 
      userId: req.user.id, 
      count: recentlyAssignedBots.length,
      assignments: recentlyAssignedBots.map(a => ({
        id: a.id,
        botId: a.botId,
        grantedAt: a.grantedAt,
        lastUsageAt: a.lastUsageAt,
        notes: a.notes
      }))
    });

    res.json({
      success: true,
      message: 'Recently assigned unused bots retrieved successfully',
      data: {
        bots: recentlyAssignedBots,
        debug: {
          userId: req.user.id,
          count: recentlyAssignedBots.length,
          assignments: recentlyAssignedBots.map(a => ({
            id: a.id,
            botId: a.botId,
            grantedAt: a.grantedAt,
            lastUsageAt: a.lastUsageAt,
            notes: a.notes
          }))
        }
      },
    });
  } catch (error) {
    logger.error('Error in getRecentlyAssignedUnusedBots', { 
      error: error.message, 
      stack: error.stack,
      userId: req.user?.id 
    });
    res.status(500).json({
      success: false,
      message: 'Internal server error',
      error: error.message
    });
  }
}); 