import { Response } from 'express';
import { AuthenticatedRequest } from '../middlewares/auth';
import { createError, asyncHandler } from '../middlewares/errorHandler';
import { ConversationService, CreateConversationInput, SaveMessageInput } from '../services/conversationService';
import { BotAccessService } from '../services/botAccessService';
import logger from '../utils/logger';

/**
 * @route   GET /api/conversations
 * @desc    Get user's conversations with pagination
 * @access  Private
 */
export const getUserConversations = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('📋 getUserConversations called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    query: req.query,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const page = parseInt(req.query.page as string) || 1;
  const limit = parseInt(req.query.limit as string) || 10;

  // Validate pagination
  if (page < 1 || limit < 1 || limit > 100) {
    logger.warn('⚠️ Invalid pagination parameters', { page, limit, userId: req.user?.id });
    throw createError('Invalid pagination parameters', 400);
  }

  const result = await ConversationService.getUserConversations(
    req.user!.id,
    req.user!.role,
    page,
    limit
  );

  logger.info('✅ getUserConversations completed', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationCount: result.conversations.length,
    totalCount: result.pagination.totalCount,
    page,
    limit,
    duration: `${Date.now() - startTime}ms`
  });

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

/**
 * @route   POST /api/conversations
 * @desc    Create a new conversation (student only)
 * @access  Private (Student)
 */
export const createConversation = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('🎯 createConversation called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    body: req.body,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const { botId, conversationMode } = req.body;

  // Validate input
  if (!botId) {
    logger.warn('❌ Missing botId in request', { userId: req.user?.id });
    throw createError('Bot ID is required', 400);
  }

  // Only students can create conversations
  if (req.user!.role !== 'STUDENT') {
    logger.warn('❌ Non-student attempted to create conversation', { 
      userId: req.user?.id, 
      userRole: req.user?.role 
    });
    throw createError('Only students can create conversations', 403);
  }

  // Check bot access
  const hasAccess = await BotAccessService.checkBotAccess(
    req.user!.id,
    botId,
    req.user!.role
  );

  if (!hasAccess) {
    logger.warn('❌ Student does not have access to bot', { 
      userId: req.user?.id, 
      botId 
    });
    throw createError('Access denied to this bot', 403);
  }

  const input: CreateConversationInput = {
    studentId: req.user!.id,
    botId,
    conversationMode: conversationMode || 'TEXT'
  };

  const conversation = await ConversationService.createConversation(input);

  logger.info('✅ createConversation completed', {
    userId: req.user?.id,
    conversationId: conversation.id,
    botName: conversation.bot.name,
    duration: `${Date.now() - startTime}ms`
  });

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

/**
 * @route   GET /api/conversations/:id
 * @desc    Get conversation by ID with messages
 * @access  Private
 */
export const getConversation = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('🔍 getConversation called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationId: req.params.id,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const { id } = req.params;

  const conversation = await ConversationService.getConversationWithMessages(
    id,
    req.user!.id,
    req.user!.role
  );

  if (!conversation) {
    logger.warn('❌ Conversation not found', { 
      conversationId: id, 
      userId: req.user?.id 
    });
    throw createError('Conversation not found', 404);
  }

  logger.info('✅ getConversation completed', {
    userId: req.user?.id,
    conversationId: id,
    messageCount: conversation._count.messages,
    botName: conversation.bot.name,
    duration: `${Date.now() - startTime}ms`
  });

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

/**
 * @route   POST /api/conversations/:id/messages
 * @desc    Save a message to conversation
 * @access  Private
 */
export const saveMessage = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('💬 saveMessage called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationId: req.params.id,
    body: req.body,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const { id } = req.params;
  const { content, senderType, messageType, audioUrl, audioDuration } = req.body;

  // Validate input
  if (!content || !senderType) {
    logger.warn('❌ Missing required fields', { 
      conversationId: id, 
      userId: req.user?.id,
      hasContent: !!content,
      hasSenderType: !!senderType
    });
    throw createError('Content and sender type are required', 400);
  }

  // Validate sender type
  if (!['USER', 'BOT'].includes(senderType)) {
    logger.warn('❌ Invalid sender type', { 
      conversationId: id, 
      senderType,
      userId: req.user?.id 
    });
    throw createError('Invalid sender type', 400);
  }

  // Check access to conversation
  const conversation = await ConversationService.getConversationWithMessages(
    id,
    req.user!.id,
    req.user!.role
  );

  if (!conversation) {
    logger.warn('❌ Conversation not found for message', { 
      conversationId: id, 
      userId: req.user?.id 
    });
    throw createError('Conversation not found', 404);
  }

  const input: SaveMessageInput = {
    conversationId: id,
    senderType,
    content,
    messageType: messageType || 'TEXT',
    audioUrl,
    audioDuration
  };

  const message = await ConversationService.saveMessage(input);

  logger.info('✅ saveMessage completed', {
    userId: req.user?.id,
    conversationId: id,
    messageId: message.id,
    senderType,
    contentLength: content.length,
    duration: `${Date.now() - startTime}ms`
  });

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

/**
 * @route   PUT /api/conversations/:id/end
 * @desc    End a conversation
 * @access  Private
 */
export const endConversation = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('🔚 endConversation called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationId: req.params.id,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const { id } = req.params;

  const conversation = await ConversationService.endConversation(
    id,
    req.user!.id,
    req.user!.role
  );

  logger.info('✅ endConversation completed', {
    userId: req.user?.id,
    conversationId: id,
    durationSeconds: conversation.durationSeconds,
    messageCount: conversation._count.messages,
    duration: `${Date.now() - startTime}ms`
  });

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

/**
 * @route   DELETE /api/conversations/:id
 * @desc    Delete conversation (admin/teacher only)
 * @access  Private (Admin/Teacher)
 */
export const deleteConversation = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('🗑️ deleteConversation called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationId: req.params.id,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const { id } = req.params;

  await ConversationService.deleteConversation(
    id,
    req.user!.id,
    req.user!.role
  );

  logger.info('✅ deleteConversation completed', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationId: id,
    duration: `${Date.now() - startTime}ms`
  });

  res.json({
    success: true,
    message: 'Conversation deleted successfully'
  });
});

/**
 * @route   PUT /api/conversations/:id
 * @desc    Update conversation (limited fields)
 * @access  Private
 */
export const updateConversation = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('✏️ updateConversation called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    conversationId: req.params.id,
    body: req.body,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  const { id } = req.params;
  const { summary, aiAnalysis, audioUrl, audioTranscript, voiceAnalysis } = req.body;

  // Check access to conversation
  const conversation = await ConversationService.getConversationWithMessages(
    id,
    req.user!.id,
    req.user!.role
  );

  if (!conversation) {
    logger.warn('❌ Conversation not found for update', { 
      conversationId: id, 
      userId: req.user?.id 
    });
    throw createError('Conversation not found', 404);
  }

  // Only allow updating specific fields
  const updateData: any = {};
  if (summary !== undefined) updateData.summary = summary;
  if (aiAnalysis !== undefined) updateData.aiAnalysis = aiAnalysis;
  if (audioUrl !== undefined) updateData.audioUrl = audioUrl;
  if (audioTranscript !== undefined) updateData.audioTranscript = audioTranscript;
  if (voiceAnalysis !== undefined) updateData.voiceAnalysis = voiceAnalysis;

  if (Object.keys(updateData).length === 0) {
    logger.warn('❌ No valid fields to update', { 
      conversationId: id, 
      userId: req.user?.id 
    });
    throw createError('No valid fields to update', 400);
  }

  // Import prisma here to avoid circular dependency
  const { prisma } = require('../index');
  
  const updatedConversation = await prisma.conversation.update({
    where: { id },
    data: updateData,
    include: {
      bot: {
        select: {
          id: true,
          name: true,
          topic: true,
          level: true,
          imageUrl: true,
        }
      },
      student: {
        select: {
          id: true,
          name: true,
          email: true,
        }
      },
      _count: {
        select: {
          messages: true
        }
      }
    }
  });

  logger.info('✅ updateConversation completed', {
    userId: req.user?.id,
    conversationId: id,
    updatedFields: Object.keys(updateData),
    duration: `${Date.now() - startTime}ms`
  });

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

/**
 * @route   GET /api/conversations/recent
 * @desc    Get user's recent conversations for recent bots
 * @access  Private
 */
export const getRecentConversations = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();
  
  logger.info('📋 getRecentConversations called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    userAgent: req.get('User-Agent'),
    origin: req.get('Origin'),
    ip: req.ip
  });

  try {
    // Import prisma here to avoid circular dependency
    const { prisma } = require('../index');
    
    // Get recent conversations for the user (last 5)
    const recentConversations = await prisma.conversation.findMany({
      where: { 
        studentId: req.user!.id 
      },
      select: {
        id: true,
        summary: true,
        aiAnalysis: true,
        elevenLabsConversationId: true,
        startedAt: true,
        updatedAt: true,
        durationSeconds: true,
        bot: {
          select: {
            id: true,
            name: true,
            topic: true,
            level: true,
            imageUrl: true,
            description: true,
          }
        }
      },
      orderBy: { 
        updatedAt: 'desc' 
      },
      take: 5
    });
    
    // Calculate durations from sessions and get last session date
    if (recentConversations.length > 0) {
      const conversationIds = recentConversations.map(c => c.id);
      const durationMap = await ConversationService.calculateDurationsForConversations(conversationIds);
      
      // Get last session for each conversation
      const lastSessions = await prisma.conversationSession.findMany({
        where: {
          conversationId: { in: conversationIds }
        },
        orderBy: { endedAt: 'desc' },
        distinct: ['conversationId'],
        select: {
          conversationId: true,
          endedAt: true
        }
      });
      
      const lastSessionMap = new Map(
        lastSessions.map(s => [s.conversationId, s.endedAt])
      );
      
      // Update durationSeconds and lastSessionDate for each conversation
      recentConversations.forEach(conv => {
        conv.durationSeconds = durationMap.get(conv.id) || 0;
        (conv as any).lastSessionDate = lastSessionMap.get(conv.id) || conv.updatedAt;
      });
    }

    logger.info('✅ getRecentConversations completed', {
      userId: req.user?.id,
      conversationCount: recentConversations.length,
      duration: `${Date.now() - startTime}ms`
    });

    res.json({
      success: true,
      data: {
        conversations: recentConversations
      }
    });
  } catch (error) {
    logger.error('❌ Error in getRecentConversations', {
      userId: req.user?.id,
      error: error instanceof Error ? error.message : 'Unknown error',
      duration: `${Date.now() - startTime}ms`
    });
    throw error;
  }
});

/**
 * @route   GET /api/conversations/admin
 * @desc    List all conversations (admin/teacher) with pagination
 * @access  Private (Admin/Teacher)
 */
export const getAllConversationsAdmin = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const startTime = Date.now();

  logger.info('📋 getAllConversationsAdmin called', {
    userId: req.user?.id,
    userRole: req.user?.role,
    query: req.query
  });

  const page = Math.max(parseInt((req.query.page as string) || '1', 10), 1);
  const limit = Math.min(Math.max(parseInt((req.query.limit as string) || '10', 10), 1), 100);
  const skip = (page - 1) * limit;

  // Import prisma here to avoid circular deps
  const { prisma } = require('../index');

  const [conversations, totalCount] = await Promise.all([
    prisma.conversation.findMany({
      include: {
        bot: { select: { id: true, name: true, topic: true, level: true, imageUrl: true } },
        student: { select: { id: true, name: true, email: true } },
        _count: { select: { messages: true } },
      },
      orderBy: { startedAt: 'desc' },
      skip,
      take: limit,
    }),
    prisma.conversation.count(),
  ]);

  logger.info('✅ getAllConversationsAdmin completed', {
    userId: req.user?.id,
    totalCount,
    page,
    limit,
    duration: `${Date.now() - startTime}ms`
  });

  res.json({
    success: true,
    data: {
      conversations,
      pagination: {
        page,
        limit,
        totalCount,
        totalPages: Math.ceil(totalCount / limit),
        hasNext: page * limit < totalCount,
        hasPrev: page > 1,
      }
    }
  });
});