import { prisma } from '../index';
import { createError } from '../middlewares/errorHandler';
import logger from '../utils/logger';

type UserRole = 'ADMIN' | 'TEACHER' | 'STUDENT';

export interface BotWithAccess {
  id: string;
  name: string;
  topic: string;
  level: string;
  imageUrl: string;
  agentId: string;
  description: string;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
  isTimerEnabled: boolean;
  maxUsageSeconds: number | null;
  hasAccess: boolean;
  accessGrantedAt?: Date;
  accessGrantedBy?: {
    id: string;
    name: string;
    email: string;
  };
  // Usage information (only for students)
  totalUsageSeconds?: number;
  isLocked?: boolean;
  remainingSeconds?: number | null;
}

export class BotAccessService {
  /**
   * Get bots accessible to a user based on their role
   */
  static async getUserAccessibleBots(userId: string, userRole: UserRole): Promise<BotWithAccess[]> {
    const startTime = Date.now();
    
    logger.info('🔍 Getting user accessible bots', {
      userId,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      if (userRole === 'ADMIN' || userRole === 'TEACHER') {
        logger.info('👑 Admin/Teacher access - getting all active bots', {
          userId,
          userRole
        });
        
        // Admins and teachers can see all active bots
        const bots = await prisma.bot.findMany({
          where: { isActive: true },
          orderBy: { createdAt: 'desc' },
        });

        logger.info('✅ All active bots retrieved for admin/teacher', {
          userId,
          userRole,
          botCount: bots.length,
          botNames: bots.map(bot => bot.name)
        });

        return bots.map(bot => ({
          ...bot,
          hasAccess: true,
        }));
      } else {
        logger.info('👤 Student access - getting bots with explicit access', {
          userId,
          userRole
        });
        
        // Students can only see bots they have access to
        const botAccesses = await prisma.studentBotAccess.findMany({
          where: { studentId: userId },
          include: {
            bot: true,
            granter: {
              select: {
                id: true,
                name: true,
                email: true,
              },
            },
          },
        });

        logger.info('📋 Student bot accesses retrieved', {
          userId,
          userRole,
          accessCount: botAccesses.length,
          botNames: botAccesses.map(access => access.bot.name)
        });

        const accessibleBots = botAccesses
          .filter(access => access.bot.isActive)
          .map(access => {
            // Calculate remaining seconds and check if locked
            const remainingSeconds = access.bot.isTimerEnabled && access.bot.maxUsageSeconds
              ? access.bot.maxUsageSeconds - access.totalUsageSeconds
              : null;
            
            const isLocked = access.isLocked || (remainingSeconds !== null && remainingSeconds <= 0);

            return {
              ...access.bot,
              hasAccess: true,
              accessGrantedAt: access.grantedAt,
              accessGrantedBy: access.granter,
              // Include usage information
              totalUsageSeconds: access.totalUsageSeconds,
              isLocked,
              remainingSeconds,
            };
          });

        logger.info('✅ Accessible bots filtered for student', {
          userId,
          userRole,
          totalAccesses: botAccesses.length,
          activeBots: accessibleBots.length,
          inactiveBots: botAccesses.length - accessibleBots.length
        });

        return accessibleBots;
      }
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error getting user accessible bots', { 
        error: error.message,
        userId,
        userRole,
        totalTime: `${totalTime}ms`,
        stack: error.stack
      });
      throw createError('Failed to retrieve accessible bots', 500);
    }
  }

  /**
   * Check if a user has access to a specific bot
   */
  static async checkBotAccess(userId: string, botId: string, userRole: UserRole): Promise<boolean> {
    const startTime = Date.now();
    
    logger.info('🔍 Checking bot access', {
      userId,
      botId,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      // Admins and teachers have access to all bots (no timer restrictions)
      if (userRole === 'ADMIN' || userRole === 'TEACHER') {
        logger.info('👑 Admin/Teacher access check - querying bot', {
          userId,
          botId,
          userRole
        });
        
        const bot = await prisma.bot.findUnique({
          where: { id: botId, isActive: true },
        });

        const hasAccess = !!bot;
        
        logger.info('✅ Admin/Teacher access check completed', {
          userId,
          botId,
          userRole,
          hasAccess,
          botName: bot?.name
        });

        return hasAccess;
      }

      logger.info('👤 Student access check - querying explicit access and timer limits', {
        userId,
        botId,
        userRole
      });

      // Students need explicit access and must pass timer checks
      const access = await prisma.studentBotAccess.findUnique({
        where: {
          studentId_botId: {
            studentId: userId,
            botId: botId,
          },
        },
        include: {
          bot: {
            select: {
              isActive: true,
              isTimerEnabled: true,
              maxUsageSeconds: true,
            },
          },
        },
      });

      if (!access || !access.bot.isActive) {
        logger.info('❌ Student access check failed - no access or bot inactive', {
          userId,
          botId,
          hasAccessRecord: !!access,
          botIsActive: access?.bot.isActive
        });
        return false;
      }

      // Check if bot is locked due to timer limits
      if (access.isLocked) {
        logger.info('🔒 Student access check failed - bot is locked due to time limit', {
          userId,
          botId,
          totalUsageSeconds: access.totalUsageSeconds,
          maxUsageSeconds: access.bot.maxUsageSeconds
        });
        return false;
      }

      // Check timer limits if enabled
      if (access.bot.isTimerEnabled && access.bot.maxUsageSeconds) {
        const remainingSeconds = access.bot.maxUsageSeconds - access.totalUsageSeconds;
        
        if (remainingSeconds <= 0) {
          logger.info('⏰ Student access check failed - time limit exceeded', {
            userId,
            botId,
            totalUsageSeconds: access.totalUsageSeconds,
            maxUsageSeconds: access.bot.maxUsageSeconds,
            remainingSeconds
          });
          
          // Auto-lock the bot access
          await prisma.studentBotAccess.update({
            where: { id: access.id },
            data: { isLocked: true }
          });
          
          return false;
        }
        
        logger.info('⏰ Timer check passed', {
          userId,
          botId,
          totalUsageSeconds: access.totalUsageSeconds,
          maxUsageSeconds: access.bot.maxUsageSeconds,
          remainingSeconds
        });
      }

      const hasAccess = true;
      
      const totalTime = Date.now() - startTime;
      
        logger.info('✅ Student access check completed', {
          userId,
          botId,
          userRole,
          hasAccess,
          hasAccessRecord: !!access,
          botIsActive: access?.bot.isActive,
          isTimerEnabled: access?.bot.isTimerEnabled,
          totalUsageSeconds: access?.totalUsageSeconds,
          maxUsageSeconds: access?.bot.maxUsageSeconds,
          totalTime: `${totalTime}ms`
        });

      return hasAccess;
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error in checkBotAccess', {
        userId,
        botId,
        userRole,
        error: error.message,
        totalTime: `${totalTime}ms`
      });
      
      throw error;
    }
  }

  /**
   * Check if a user has access to a bot by name
   */
  static async checkBotAccessByName(userId: string, botName: string, userRole: UserRole): Promise<boolean> {
    const startTime = Date.now();
    
    logger.info('🔍 Checking bot access by name', {
      userId,
      botName,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      // Admins and teachers have access to all bots
      if (userRole === 'ADMIN' || userRole === 'TEACHER') {
        logger.info('👑 Admin/Teacher access check by name - querying bot', {
          userId,
          botName,
          userRole
        });
        
        const bot = await prisma.bot.findFirst({
          where: { name: botName, isActive: true },
        });

        const hasAccess = !!bot;
        
        logger.info('✅ Admin/Teacher access check by name completed', {
          userId,
          botName,
          userRole,
          hasAccess,
          botId: bot?.id
        });

        return hasAccess;
      }

      logger.info('👤 Student access check by name - querying explicit access', {
        userId,
        botName,
        userRole
      });

      // Students need explicit access
      const access = await prisma.studentBotAccess.findFirst({
        where: {
          studentId: userId,
          bot: {
            name: botName,
            isActive: true,
          },
        },
        include: {
          bot: {
            select: {
              id: true,
              isActive: true,
            },
          },
        },
      });

      const hasAccess = !!access;
      
      const totalTime = Date.now() - startTime;
      
      logger.info('✅ Student access check by name completed', {
        userId,
        botName,
        userRole,
        hasAccess,
        hasAccessRecord: !!access,
        botId: access?.bot.id,
        totalTime: `${totalTime}ms`
      });

      return hasAccess;
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error checking bot access by name', { 
        error: error.message,
        userId,
        botName,
        userRole,
        totalTime: `${totalTime}ms`,
        stack: error.stack
      });
      throw createError('Failed to check bot access', 500);
    }
  }

  /**
   * Get bot by name with access check
   */
  static async getBotByNameWithAccess(userId: string, botName: string, userRole: UserRole) {
    const startTime = Date.now();
    
    logger.info('🔍 Getting bot by name with access check', {
      userId,
      botName,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      if (userRole === 'ADMIN' || userRole === 'TEACHER') {
        logger.info('👑 Admin/Teacher - getting bot by name', {
          userId,
          botName,
          userRole
        });
        
        const bot = await prisma.bot.findFirst({
          where: { name: botName, isActive: true },
        });

        if (!bot) {
          logger.warn('⚠️ Bot not found for admin/teacher', {
            userId,
            botName,
            userRole
          });
          return null;
        }

        logger.info('✅ Bot found for admin/teacher', {
          userId,
          botName,
          botId: bot.id,
          userRole
        });

        return {
          ...bot,
          hasAccess: true,
        };
      } else {
        logger.info('👤 Student - getting bot by name with access check', {
          userId,
          botName,
          userRole
        });
        
        const access = await prisma.studentBotAccess.findFirst({
          where: {
            studentId: userId,
            bot: {
              name: botName,
              isActive: true,
            },
          },
          include: {
            bot: true,
            granter: {
              select: {
                id: true,
                name: true,
                email: true,
              },
            },
          },
        });

        if (!access) {
          logger.warn('⚠️ Bot access not found for student', {
            userId,
            botName,
            userRole
          });
          return null;
        }

        const totalTime = Date.now() - startTime;
        
        logger.info('✅ Bot found with access for student', {
          userId,
          botName,
          botId: access.bot.id,
          userRole,
          grantedAt: access.grantedAt,
          grantedBy: access.granter.name,
          totalTime: `${totalTime}ms`
        });

        return {
          ...access.bot,
          hasAccess: true,
          accessGrantedAt: access.grantedAt,
          accessGrantedBy: access.granter,
        };
      }
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error getting bot by name with access', { 
        error: error.message,
        userId,
        botName,
        userRole,
        totalTime: `${totalTime}ms`,
        stack: error.stack
      });
      throw createError('Failed to get bot with access', 500);
    }
  }

  /**
   * Grant bot access to a student
   */
  static async grantBotAccess(
    studentId: string,
    botId: string,
    grantedBy: string,
    granterRole: UserRole
  ): Promise<void> {
    const startTime = Date.now();
    
    logger.info('🔓 Granting bot access', {
      studentId,
      botId,
      grantedBy,
      granterRole,
      requestTime: new Date().toISOString()
    });

    try {
      // Check if student exists
      logger.info('🔍 Checking if student exists', { studentId });
      
      const student = await prisma.user.findUnique({
        where: { id: studentId },
        select: { id: true, name: true, email: true, role: true },
      });

      if (!student) {
        logger.warn('❌ Grant access failed - student not found', {
          studentId,
          grantedBy,
          granterRole
        });
        throw createError('Student not found', 404);
      }

      if (student.role !== 'STUDENT') {
        logger.warn('❌ Grant access failed - user is not a student', {
          studentId,
          studentRole: student.role,
          grantedBy,
          granterRole
        });
        throw createError('Can only grant access to students', 400);
      }

      logger.info('✅ Student found and validated', {
        studentId,
        studentName: student.name,
        studentEmail: student.email,
        studentRole: student.role
      });

      // Check if bot exists
      logger.info('🔍 Checking if bot exists', { botId });
      
      const bot = await prisma.bot.findUnique({
        where: { id: botId },
        select: { id: true, name: true, isActive: true },
      });

      if (!bot) {
        logger.warn('❌ Grant access failed - bot not found', {
          botId,
          studentId,
          grantedBy
        });
        throw createError('Bot not found', 404);
      }

      if (!bot.isActive) {
        logger.warn('❌ Grant access failed - bot is inactive', {
          botId,
          botName: bot.name,
          studentId,
          grantedBy
        });
        throw createError('Cannot grant access to inactive bot', 400);
      }

      logger.info('✅ Bot found and validated', {
        botId,
        botName: bot.name,
        isActive: bot.isActive
      });

      // Check if access already exists
      logger.info('🔍 Checking if access already exists', {
        studentId,
        botId
      });
      
      const existingAccess = await prisma.studentBotAccess.findUnique({
        where: {
          studentId_botId: {
            studentId,
            botId,
          },
        },
      });

      if (existingAccess) {
        logger.warn('⚠️ Access already exists', {
          studentId,
          botId,
          existingAccessId: existingAccess.id,
          grantedAt: existingAccess.grantedAt
        });
        return; // Access already granted
      }

      // Grant access
      logger.info('📝 Creating bot access record', {
        studentId,
        botId,
        grantedBy,
        granterRole
      });
      
      const access = await prisma.studentBotAccess.create({
        data: {
          studentId,
          botId,
          grantedBy,
        },
        include: {
          student: {
            select: { name: true, email: true },
          },
          bot: {
            select: { name: true },
          },
          granter: {
            select: { name: true, email: true },
          },
        },
      });

      const totalTime = Date.now() - startTime;
      
      logger.info('🎉 Bot access granted successfully', {
        accessId: access.id,
        studentId,
        studentName: access.student.name,
        botId,
        botName: access.bot.name,
        grantedBy,
        granterName: access.granter.name,
        grantedAt: access.grantedAt,
        totalTime: `${totalTime}ms`
      });
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error granting bot access', { 
        error: error.message,
        studentId,
        botId,
        grantedBy,
        granterRole,
        totalTime: `${totalTime}ms`,
        stack: error.stack
      });
      throw error;
    }
  }

  /**
   * Revoke bot access from a student
   */
  static async revokeBotAccess(
    studentId: string,
    botId: string,
    revokedBy: string,
    revokerRole: UserRole
  ): Promise<void> {
    const startTime = Date.now();
    
    logger.info('🔒 Revoking bot access', {
      studentId,
      botId,
      revokedBy,
      revokerRole,
      requestTime: new Date().toISOString()
    });

    try {
      // Check if access exists
      logger.info('🔍 Checking if access exists', {
        studentId,
        botId
      });
      
      const access = await prisma.studentBotAccess.findUnique({
        where: {
          studentId_botId: {
            studentId,
            botId,
          },
        },
        include: {
          student: {
            select: { name: true, email: true },
          },
          bot: {
            select: { name: true },
          },
          granter: {
            select: { name: true, email: true },
          },
        },
      });

      if (!access) {
        logger.warn('⚠️ Access not found for revocation', {
          studentId,
          botId,
          revokedBy
        });
        return; // Access doesn't exist, nothing to revoke
      }

      logger.info('✅ Access found for revocation', {
        accessId: access.id,
        studentName: access.student.name,
        botName: access.bot.name,
        grantedAt: access.grantedAt,
        grantedBy: access.granter.name
      });

      // Revoke access
      logger.info('🗑️ Deleting bot access record', {
        accessId: access.id,
        studentId,
        botId,
        revokedBy
      });
      
      await prisma.studentBotAccess.delete({
        where: {
          studentId_botId: {
            studentId,
            botId,
          },
        },
      });

      const totalTime = Date.now() - startTime;
      
      logger.info('🎉 Bot access revoked successfully', {
        accessId: access.id,
        studentId,
        studentName: access.student.name,
        botId,
        botName: access.bot.name,
        revokedBy,
        grantedAt: access.grantedAt,
        revokedAt: new Date(),
        totalTime: `${totalTime}ms`
      });
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error revoking bot access', { 
        error: error.message,
        studentId,
        botId,
        revokedBy,
        revokerRole,
        totalTime: `${totalTime}ms`,
        stack: error.stack
      });
      throw error;
    }
  }

  /**
   * Get students with access to a specific bot
   */
  static async getStudentsWithBotAccess(botId: string): Promise<any[]> {
    const startTime = Date.now();
    
    logger.info('👥 Getting students with bot access', {
      botId,
      requestTime: new Date().toISOString()
    });

    try {
      // Check if bot exists
      logger.info('🔍 Checking if bot exists', { botId });
      
      const bot = await prisma.bot.findUnique({
        where: { id: botId },
        select: { id: true, name: true, isActive: true },
      });

      if (!bot) {
        logger.warn('❌ Get students failed - bot not found', { botId });
        throw createError('Bot not found', 404);
      }

      logger.info('✅ Bot found', {
        botId,
        botName: bot.name,
        isActive: bot.isActive
      });

      // Get students with access
      logger.info('🔍 Querying students with access', { botId });
      
      const students = await prisma.studentBotAccess.findMany({
        where: { botId },
        include: {
          student: {
            select: {
              id: true,
              name: true,
              email: true,
              role: true,
            },
          },
          granter: {
            select: {
              id: true,
              name: true,
              email: true,
            },
          },
        },
        orderBy: { grantedAt: 'desc' },
      });

      const totalTime = Date.now() - startTime;
      
      logger.info('✅ Students with bot access retrieved', {
        botId,
        botName: bot.name,
        studentCount: students.length,
        studentNames: students.map(s => s.student.name),
        totalTime: `${totalTime}ms`
      });

      return students;
    } catch (error) {
      const totalTime = Date.now() - startTime;
      
      logger.error('❌ Error getting students with bot access', { 
        error: error.message,
        botId,
        totalTime: `${totalTime}ms`,
        stack: error.stack
      });
      throw error;
    }
  }
}

