import { prisma } from '../index';
import { createError } from '../middlewares/errorHandler';
// Use string literal types to avoid runtime enum import issues
import logger from '../utils/logger';
import { QuotaService } from './quotaService';

type UserRole = 'ADMIN' | 'TEACHER' | 'STUDENT';
type MessageSender = 'USER' | 'BOT';
type MessageType = 'TEXT' | 'VOICE' | 'AUDIO';
type ConversationMode = 'TEXT' | 'VOICE' | 'MIXED';

export interface CreateConversationInput {
  studentId: string;
  botId: string;
  conversationMode?: ConversationMode;
}

export interface SaveMessageInput {
  conversationId: string;
  senderType: MessageSender;
  content: string;
  messageType?: MessageType;
  audioUrl?: string;
  audioDuration?: number;
}

export interface ConversationWithMessages {
  id: string;
  studentId: string;
  botId: string;
  transcript?: string;
  startedAt: Date;
  endedAt?: Date;
  durationSeconds?: number;
  audioUrl?: string;
  summary?: string;
  aiAnalysis?: string;
  elevenLabsConversationId?: string;
  audioTranscript?: string;
  voiceAnalysis?: string;
  conversationMode: ConversationMode;
  createdAt: Date;
  updatedAt: Date;
  bot: {
    id: string;
    name: string;
    topic: string;
    level: string;
    imageUrl: string;
    agentId: string;
    description: string;
  };
  student: {
    id: string;
    name: string;
    email: string;
    role: string;
  };
  messages: Array<{
    id: string;
    conversationId: string;
    senderType: MessageSender;
    content: string;
    messageType: MessageType;
    audioUrl?: string;
    audioDuration?: number;
    timestamp: Date;
    createdAt: Date;
    updatedAt: Date;
  }>;
  _count: {
    messages: number;
  };
}

export class ConversationService {
  /**
   * Calculate total duration from all sessions of a conversation
   */
  static async calculateDurationFromSessions(conversationId: string): Promise<number> {
    const sessions = await prisma.conversationSession.findMany({
      where: { 
        conversationId,
        durationSeconds: { not: null }
      },
      select: { durationSeconds: true }
    });
    
    return sessions.reduce((total, session) => {
      return total + (session.durationSeconds || 0);
    }, 0);
  }

  /**
   * Calculate total duration for multiple conversations
   */
  static async calculateDurationsForConversations(conversationIds: string[]): Promise<Map<string, number>> {
    const sessions = await prisma.conversationSession.findMany({
      where: { 
        conversationId: { in: conversationIds },
        durationSeconds: { not: null }
      },
      select: { 
        conversationId: true,
        durationSeconds: true 
      }
    });
    
    const durationMap = new Map<string, number>();
    
    // Initialize all conversation IDs with 0
    conversationIds.forEach(id => durationMap.set(id, 0));
    
    // Sum up sessions for each conversation
    sessions.forEach(session => {
      const current = durationMap.get(session.conversationId) || 0;
      durationMap.set(session.conversationId, current + (session.durationSeconds || 0));
    });
    
    return durationMap;
  }

  /**
   * Create a new conversation
   */
  static async createConversation(input: CreateConversationInput): Promise<any> {
    const startTime = Date.now();
    
    logger.info('🎯 Creating new conversation', {
      studentId: input.studentId,
      botId: input.botId,
      conversationMode: input.conversationMode || 'TEXT',
      requestTime: new Date().toISOString()
    });

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

      if (!bot) {
        logger.warn('❌ Bot not found', { botId: input.botId });
        throw createError('Bot not found', 404);
      }

      if (!bot.isActive) {
        logger.warn('❌ Bot is not active', { botId: input.botId, botName: bot.name });
        throw createError('Bot is not active', 400);
      }

      // Verify student exists
      const student = await prisma.user.findUnique({
        where: { id: input.studentId },
        select: { id: true, name: true, role: true }
      });

      if (!student) {
        logger.warn('❌ Student not found', { studentId: input.studentId });
        throw createError('Student not found', 404);
      }

      // Create conversation
      const conversation = await prisma.conversation.create({
        data: {
          studentId: input.studentId,
          botId: input.botId,
          startedAt: new Date(),
          conversationMode: input.conversationMode || 'TEXT',
        },
        include: {
          bot: {
            select: {
              id: true,
              name: true,
              topic: true,
              level: true,
              imageUrl: true,
              agentId: true,
              description: true,
            }
          },
          student: {
            select: {
              id: true,
              name: true,
              email: true,
              role: true,
            }
          },
          _count: {
            select: {
              messages: true
            }
          }
        }
      });

      logger.info('✅ Conversation created successfully', {
        conversationId: conversation.id,
        botName: conversation.bot.name,
        studentName: conversation.student.name,
        conversationMode: conversation.conversationMode,
        duration: `${Date.now() - startTime}ms`
      });

      return conversation;

    } catch (error) {
      logger.error('❌ Error creating conversation', {
        studentId: input.studentId,
        botId: input.botId,
        error: error.message,
        stack: error.stack,
        duration: `${Date.now() - startTime}ms`
      });
      throw error;
    }
  }

  /**
   * Save a message to the database
   */
  static async saveMessage(input: SaveMessageInput): Promise<any> {
    const startTime = Date.now();
    
    logger.info('💬 Saving message', {
      conversationId: input.conversationId,
      senderType: input.senderType,
      messageType: input.messageType || 'TEXT',
      contentLength: input.content.length,
      hasAudio: !!input.audioUrl,
      requestTime: new Date().toISOString()
    });

    try {
      // Verify conversation exists
      const conversation = await prisma.conversation.findUnique({
        where: { id: input.conversationId },
        select: { id: true, endedAt: true }
      });

      if (!conversation) {
        logger.warn('❌ Conversation not found', { conversationId: input.conversationId });
        throw createError('Conversation not found', 404);
      }

      if (conversation.endedAt) {
        logger.warn('❌ Cannot add message to ended conversation', { 
          conversationId: input.conversationId,
          endedAt: conversation.endedAt
        });
        throw createError('Cannot add message to ended conversation', 400);
      }

      // Create message
      const message = await prisma.message.create({
        data: {
          conversationId: input.conversationId,
          senderType: input.senderType,
          content: input.content,
          messageType: input.messageType || 'TEXT',
          audioUrl: input.audioUrl,
          audioDuration: input.audioDuration,
          timestamp: new Date(),
        }
      });

      // Update conversation transcript (append to existing)
      const existingConversation = await prisma.conversation.findUnique({
        where: { id: input.conversationId },
        select: { transcript: true }
      });

      const senderLabel = input.senderType === 'USER' ? 'Student' : 'Bot';
      const newTranscriptLine = `${senderLabel}: ${input.content}`;
      const updatedTranscript = existingConversation?.transcript 
        ? `${existingConversation.transcript}\n${newTranscriptLine}`
        : newTranscriptLine;

      await prisma.conversation.update({
        where: { id: input.conversationId },
        data: { transcript: updatedTranscript }
      });

      logger.info('✅ Message saved successfully', {
        messageId: message.id,
        conversationId: input.conversationId,
        senderType: input.senderType,
        contentLength: input.content.length,
        duration: `${Date.now() - startTime}ms`
      });

      return message;

    } catch (error) {
      logger.error('❌ Error saving message', {
        conversationId: input.conversationId,
        senderType: input.senderType,
        error: error.message,
        stack: error.stack,
        duration: `${Date.now() - startTime}ms`
      });
      throw error;
    }
  }

  /**
   * Get conversation with messages
   */
  static async getConversationWithMessages(conversationId: string, userId: string, userRole: UserRole): Promise<ConversationWithMessages | null> {
    const startTime = Date.now();
    
    logger.info('🔍 Getting conversation with messages', {
      conversationId,
      userId,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      const conversation = await prisma.conversation.findUnique({
        where: { id: conversationId },
        include: {
          bot: {
            select: {
              id: true,
              name: true,
              topic: true,
              level: true,
              imageUrl: true,
              agentId: true,
              description: true,
            }
          },
          student: {
            select: {
              id: true,
              name: true,
              email: true,
              role: true,
            }
          },
          messages: {
            orderBy: { timestamp: 'asc' },
            select: {
              id: true,
              conversationId: true,
              senderType: true,
              content: true,
              messageType: true,
              audioUrl: true,
              audioDuration: true,
              timestamp: true,
              createdAt: true,
              updatedAt: true,
            }
          },
          _count: {
            select: {
              messages: true
            }
          }
        }
      });

      if (!conversation) {
        logger.warn('❌ Conversation not found', { conversationId });
        return null;
      }

      // Check access permissions - all users can only access their own conversations
      if (conversation.studentId !== userId) {
        logger.warn('❌ Access denied to conversation', { 
          conversationId, 
          studentId: conversation.studentId, 
          userId,
          userRole
        });
        throw createError('Access denied', 403);
      }

      logger.info('✅ Conversation retrieved successfully', {
        conversationId,
        messageCount: conversation._count.messages,
        botName: conversation.bot.name,
        studentName: conversation.student.name,
        duration: `${Date.now() - startTime}ms`
      });

      return conversation as ConversationWithMessages;

    } catch (error) {
      logger.error('❌ Error getting conversation', {
        conversationId,
        userId,
        userRole,
        error: error.message,
        stack: error.stack,
        duration: `${Date.now() - startTime}ms`
      });
      throw error;
    }
  }

  /**
   * Get user's conversations with pagination
   */
  static async getUserConversations(
    userId: string, 
    userRole: UserRole, 
    page: number = 1, 
    limit: number = 10
  ): Promise<{ conversations: any[]; pagination: any }> {
    const startTime = Date.now();
    
    logger.info('📋 Getting user conversations', {
      userId,
      userRole,
      page,
      limit,
      requestTime: new Date().toISOString()
    });

    try {
      const skip = (page - 1) * limit;
      // All users should only see their own conversations for privacy
      // If admins need to see all conversations, use a separate admin endpoint
      const whereClause = { studentId: userId };

      const [conversations, totalCount] = await Promise.all([
        prisma.conversation.findMany({
          where: whereClause,
          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({ where: whereClause })
      ]);

      const totalPages = Math.ceil(totalCount / limit);
      
      // Calculate durations from sessions and get last session dates
      if (conversations.length > 0) {
        const conversationIds = conversations.map(c => c.id);
        
        // Get durations
        const durationMap = await this.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 conversations with calculated values
        conversations.forEach(conv => {
          conv.durationSeconds = durationMap.get(conv.id) || 0;
          (conv as any).lastSessionDate = lastSessionMap.get(conv.id) || conv.updatedAt;
        });
      }

      logger.info('✅ User conversations retrieved', {
        userId,
        userRole,
        conversationCount: conversations.length,
        totalCount,
        page,
        limit,
        totalPages,
        duration: `${Date.now() - startTime}ms`
      });

      return {
        conversations,
        pagination: {
          page,
          limit,
          totalCount,
          totalPages,
          hasNext: page < totalPages,
          hasPrev: page > 1,
        }
      };

    } catch (error) {
      logger.error('❌ Error getting user conversations', {
        userId,
        userRole,
        error: error.message,
        stack: error.stack,
        duration: `${Date.now() - startTime}ms`
      });
      throw error;
    }
  }

  /**
   * End a conversation
   */
  static async endConversation(conversationId: string, userId: string, userRole: UserRole): Promise<any> {
    const startTime = Date.now();
    
    logger.info('🔚 Ending conversation', {
      conversationId,
      userId,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      // Get conversation with access check
      const conversation = await this.getConversationWithMessages(conversationId, userId, userRole);
      
      if (!conversation) {
        throw createError('Conversation not found', 404);
      }

      // Calculate duration by summing all completed sessions
      const sessions = await prisma.conversationSession.findMany({
        where: { 
          conversationId,
          durationSeconds: { not: null }
        },
        select: { durationSeconds: true }
      });
      
      const durationSeconds = sessions.reduce((total, session) => {
        return total + (session.durationSeconds || 0);
      }, 0);
      
      logger.info('📊 Calculated total duration from sessions', {
        conversationId,
        sessionCount: sessions.length,
        durationSeconds
      });

      // Update conversation (end)
      const updatedConversation = await prisma.conversation.update({
        where: { id: conversationId },
        data: {
          endedAt: new Date(),
          durationSeconds,
        },
        include: {
          bot: {
            select: {
              id: true,
              name: true,
              topic: true,
              level: true,
              imageUrl: true,
              description: true,
              agentId: true,
            }
          },
          student: {
            select: {
              id: true,
              name: true,
              email: true,
            }
          },
          _count: {
            select: {
              messages: true
            }
          }
        }
      });

      // Auto-ingest evaluation/feedback from ElevenLabs if we have their conversation id
      try {
        if (updatedConversation.elevenLabsConversationId) {
          const externalId = updatedConversation.elevenLabsConversationId;
          const { elevenLabsService } = await import('./elevenLabsService');
          const details: any = await elevenLabsService.getConversationDetails(externalId);

          // Normalize evaluations from multiple possible shapes
          const evaluationMap = details?.analysis?.evaluation_criteria_results
            || details?.analysis?.evaluation_results
            || details?.evaluations
            || {};
          let evaluations = Array.isArray(evaluationMap)
            ? evaluationMap
            : Object.keys(evaluationMap || {}).map((key) => ({
                identifier: key,
                result: evaluationMap[key]?.result || evaluationMap[key]?.status || 'unknown',
                rationale: evaluationMap[key]?.rationale || evaluationMap[key]?.reason || ''
              }));

          if (evaluations.length) {
            const { resolveEvaluationIdentifiers } = await import('../utils/evaluationCriteria');
            evaluations = await resolveEvaluationIdentifiers(updatedConversation.bot?.agentId, evaluations);
          }

          // Try to capture summary from various fields
          const computedSummary = (
            details?.call_summary_title
            || details?.data?.call_summary_title
            || details?.summary
            || details?.overview?.summary
          );
          const summary = computedSummary || 'Evaluation received';

          // Prepare transcript text block
          const transcriptArr = details?.transcript || details?.data?.transcript || [];
          const transcriptText = Array.isArray(transcriptArr)
            ? transcriptArr.map((entry: any, i: number) => {
                const role = (entry?.role === 'agent' ? 'Agent' : (entry?.role ? String(entry.role).charAt(0).toUpperCase() + String(entry.role).slice(1) : 'User'));
                const msg = entry?.message || entry?.text || '';
                const t = entry?.time_in_call_secs ? ` (${entry.time_in_call_secs}s)` : '';
                return `${i + 1}. ${role}${t}: ${msg}`;
              }).join('\n')
            : '';

          // F4: translate the transcript summary (and its DB fallback) to Spanish
          const { openaiService } = await import('./openaiService');
          const { analysis: translatedAnalysisObj, summary: translatedSummary } =
            await openaiService.translateSummaryFields(details?.analysis, summary);

          const aiAnalysis = JSON.stringify({
            conversation_id: details?.conversation_id || details?.id,
            overview: details?.overview || {},
            call_summary_title: details?.call_summary_title || details?.data?.call_summary_title,
            transcript: transcriptArr,
            analysis: translatedAnalysisObj,
            evaluations,
            raw_payload: details
          }, null, 2);

          await prisma.conversation.update({
            where: { id: conversationId },
            data: {
              summary: translatedSummary,
              aiAnalysis,
              audioTranscript: transcriptText || undefined
            }
          });
          logger.info('✅ Auto-ingested ElevenLabs feedback on conversation end', { conversationId, externalId });
        } else {
          logger.info('ℹ️ Skipping auto-ingest: no elevenLabsConversationId on conversation', { conversationId });
        }
      } catch (e) {
        logger.warn('⚠️ Auto-ingest failed (will rely on manual ensure/webhook)', { conversationId, error: (e as any).message });
      }

      // Intentar generar feedback de OpenAI automáticamente si tenemos un transcript disponible
      try {
        // Verificar estado actual con bloqueo atómico para evitar duplicados
        const currentState = await prisma.conversation.findUnique({
          where: { id: conversationId },
          select: { 
            audioTranscript: true, 
            transcript: true, 
            openaiSummary: true, 
            openaiFeedback: true, 
            feedbackStatus: true 
          }
        });

        // Idempotencia robusta: si ya existe feedback, no generar de nuevo
        if (currentState && (
          currentState.feedbackStatus === 'completed' || 
          currentState.feedbackStatus === 'in_progress' ||
          (currentState as any).openaiSummary || 
          (currentState as any).openaiFeedback
        )) {
          logger.info('ℹ️ Skipping OpenAI auto-feedback: already completed or in progress', { 
            conversationId, 
            feedbackStatus: currentState.feedbackStatus,
            hasSummary: !!(currentState as any).openaiSummary,
            hasFeedback: !!(currentState as any).openaiFeedback
          });
        } else {
          const text = (currentState?.audioTranscript || currentState?.transcript || '').toString().trim();
          if (!text.length) {
            logger.info('ℹ️ Skipping OpenAI auto-feedback: no transcript text available', { conversationId });
          } else {
            // Bloqueo atómico: marcar en progreso solo si no está ya en progreso
            const updateResult = await prisma.conversation.updateMany({
              where: { 
                id: conversationId,
                feedbackStatus: { not: 'in_progress' } // Solo actualizar si no está en progreso
              },
              data: { feedbackStatus: 'in_progress' } as any
            });

            // Solo proceder si realmente actualizamos (evita condiciones de carrera)
            if (updateResult.count > 0) {
              logger.info('🚀 Starting OpenAI feedback generation', { conversationId });
              
              const { openaiService } = await import('./openaiService');
              const fb = await openaiService.generateFeedback(
                text,
                updatedConversation.bot.topic,
                (updatedConversation.bot as any).feedback || '',
                updatedConversation.bot.level
              );

              await prisma.conversation.update({
                where: { id: conversationId },
                data: {
                  openaiSummary: fb.summary as any,
                  openaiEvaluation: fb.evaluation as any,
                  openaiFeedback: fb.feedback as any,
                  openaiTranscript: fb.transcript as any,
                  feedbackStatus: 'completed'
                } as any
              });
              
              logger.info('✅ OpenAI feedback auto-generated on conversation end', { conversationId });
            } else {
              logger.info('ℹ️ OpenAI feedback generation already in progress, skipping', { conversationId });
            }
          }
        }
      } catch (oaErr: any) {
        logger.warn('⚠️ OpenAI auto-feedback failed on conversation end', { conversationId, error: oaErr?.message });
      }

      // Register usage in ledger for students  
      if (durationSeconds > 0 && updatedConversation.studentId) {
        try {
          await QuotaService.recordUsage(
            updatedConversation.studentId,
            durationSeconds,
            'conversation_end',
            'realtime',
            conversationId
          );
          
          logger.info('📝 Usage recorded in quota ledger', {
            conversationId,
            studentId: updatedConversation.studentId,
            durationSeconds,
            source: 'realtime'
          });
        } catch (usageError: any) {
          logger.warn('⚠️ Failed to record usage in quota ledger (conversation still ended)', {
            conversationId,
            studentId: updatedConversation.studentId,
            durationSeconds,
            error: usageError?.message,
            stack: usageError?.stack
          });
        }
      }

      logger.info('✅ Conversation ended successfully', {
        conversationId,
        durationSeconds,
        messageCount: updatedConversation._count.messages,
        botName: updatedConversation.bot.name,
        studentName: updatedConversation.student.name,
        duration: `${Date.now() - startTime}ms`
      });

      return updatedConversation;

    } catch (error) {
      logger.error('❌ Error ending conversation', {
        conversationId,
        userId,
        userRole,
        error: error.message,
        stack: error.stack,
        duration: `${Date.now() - startTime}ms`
      });
      throw error;
    }
  }

  /**
   * Delete a conversation (admin/teacher only)
   */
  static async deleteConversation(conversationId: string, userId: string, userRole: UserRole): Promise<void> {
    const startTime = Date.now();
    
    logger.info('🗑️ Deleting conversation', {
      conversationId,
      userId,
      userRole,
      requestTime: new Date().toISOString()
    });

    try {
      // Only admins and teachers can delete conversations
      if (userRole === 'STUDENT') {
        logger.warn('❌ Student attempted to delete conversation', { conversationId, userId });
        throw createError('Insufficient permissions', 403);
      }

      // Check if conversation exists
      const conversation = await prisma.conversation.findUnique({
        where: { id: conversationId },
        select: { id: true, studentId: true, botId: true }
      });

      if (!conversation) {
        logger.warn('❌ Conversation not found for deletion', { conversationId });
        throw createError('Conversation not found', 404);
      }

      // Delete conversation (messages will be cascaded)
      await prisma.conversation.delete({
        where: { id: conversationId }
      });

      logger.info('✅ Conversation deleted successfully', {
        conversationId,
        deletedBy: userId,
        userRole,
        duration: `${Date.now() - startTime}ms`
      });

    } catch (error) {
      logger.error('❌ Error deleting conversation', {
        conversationId,
        userId,
        userRole,
        error: error.message,
        stack: error.stack,
        duration: `${Date.now() - startTime}ms`
      });
      throw error;
    }
  }
}
