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

// Validation schemas
const updateApiKeySchema = z.object({
  apiKey: z.string().min(1, 'API key is required'),
});

// Empty string is valid — it clears a previously-saved assistant ID.
const updateAssistantIdSchema = z.object({
  assistantId: z.string(),
});

const generateFeedbackSchema = z.object({
  conversationId: z.string().min(1, 'Conversation ID is required'),
  transcript: z.string().min(1, 'Transcript is required'),
  botId: z.string().min(1, 'Bot ID is required'),
});

// Get current OpenAI configuration (accessible to all authenticated users)
export const getConfig = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const user = req.user;
  
  if (!user) {
    throw createError('User not authenticated', 401);
  }
  
  const config = await openaiService.getCurrentConfig();
  
  logger.info('OpenAI configuration retrieved', {
    hasApiKey: config.hasApiKey,
    hasAssistantId: config.hasAssistantId,
    isValid: config.isValid,
    userRole: user.role,
    userId: user.id
  });
  
  // For non-admin users, only return basic availability info
  if (user.role !== 'ADMIN') {
    res.json({
      success: true,
      data: {
        hasApiKey: config.hasApiKey,
        hasAssistantId: config.hasAssistantId,
        isValid: config.isValid,
        // Don't expose sensitive information to non-admin users
        configuredAt: undefined,
        maskedApiKey: undefined,
        maskedAssistantId: undefined,
      },
    });
  } else {
    // For admin users, return full configuration details
    res.json({
      success: true,
      data: {
        hasApiKey: config.hasApiKey,
        hasAssistantId: config.hasAssistantId,
        isValid: config.isValid,
        configuredAt: config.createdAt,
        maskedApiKey: config.maskedApiKey,
        maskedAssistantId: config.maskedAssistantId,
      },
    });
  }
});

// Update just the API key, keeping any already-saved assistant ID (admin only)
export const updateApiKey = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const { apiKey } = updateApiKeySchema.parse(req.body);

  await openaiService.updateApiKey(apiKey);

  logger.info('OpenAI API key updated successfully');

  res.json({
    success: true,
    message: 'API key updated successfully',
  });
});

// Update just the assistant ID, keeping the already-saved API key (admin only)
export const updateAssistantId = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const { assistantId } = updateAssistantIdSchema.parse(req.body);

  await openaiService.updateAssistantId(assistantId);

  logger.info('OpenAI Assistant ID updated successfully');

  res.json({
    success: true,
    message: 'Assistant ID updated successfully',
  });
});

// Test OpenAI connection (admin only)
export const testConnection = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const isValid = await openaiService.testConnection();
  
  logger.info('OpenAI connection test completed', {
    isValid,
    userId: req.user?.id
  });
  
  res.json({
    success: true,
    data: { isValid },
  });
});

// Generate conversation feedback using OpenAI
export const generateFeedback = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const { conversationId, transcript, botId } = generateFeedbackSchema.parse(req.body);
  const user = req.user;
  
  if (!user) {
    throw createError('User not authenticated', 401);
  }
  
  // Validate that the user has access to the bot
  const hasAccess = await BotAccessService.checkBotAccess(user.id, botId, user.role);
  if (!hasAccess) {
    throw createError('Access denied to this bot', 403);
  }
  
  // Get the bot to access its information
  const bot = await prisma.bot.findUnique({
    where: { id: botId }
  });
  
  if (!bot) {
    throw createError('Bot not found', 404);
  }
  
  logger.info('Generating conversation feedback with OpenAI', {
    userId: user.id,
    conversationId,
    botId,
    transcriptLength: transcript.length
  });
  
  // Idempotencia: si ya existe feedback completado, devolverlo
  try {
    const existing: any = await prisma.conversation.findUnique({
      where: { id: conversationId },
      select: { openaiSummary: true, openaiEvaluation: true, openaiFeedback: true, openaiTranscript: true, feedbackStatus: true } as any
    });
    if (existing && (existing.feedbackStatus === 'completed' || existing.openaiSummary || existing.openaiFeedback)) {
      return res.json({
        success: true,
        data: {
          conversationId,
          summary: existing.openaiSummary,
          evaluation: existing.openaiEvaluation,
          feedback: existing.openaiFeedback,
          transcript: existing.openaiTranscript,
          feedbackStatus: existing.feedbackStatus || 'completed'
        }
      });
    }
  } catch {}

  // Marcar en progreso para evitar duplicados concurrentes
  try {
    await prisma.conversation.update({
      where: { id: conversationId },
      data: { feedbackStatus: 'in_progress' } as any
    });
  } catch {}

  // Generate feedback from OpenAI
  const feedbackData = await openaiService.generateFeedback(
    transcript,
    bot.topic,
    bot.feedback || '',
    bot.level
  );
  
  // Get existing conversation to calculate duration
  const existingConversation = await prisma.conversation.findUnique({
    where: { id: conversationId },
    select: { startedAt: true }
  });
  
  const now = new Date();
  const startedAt = existingConversation?.startedAt || now;
  const durationSeconds = Math.floor((now.getTime() - startedAt.getTime()) / 1000);
  
  // Create or update conversation record in database
  const conversation = await prisma.conversation.upsert({
    where: {
      id: conversationId
    },
    update: {
      transcript: transcript,
      // Campos OpenAI (no definidos en tipos del cliente actual) – cast a any
      openaiSummary: feedbackData.summary,
      openaiEvaluation: feedbackData.evaluation,
      openaiFeedback: feedbackData.feedback,
      openaiTranscript: feedbackData.transcript,
      feedbackStatus: 'completed',
      endedAt: now,
      durationSeconds: durationSeconds,
      conversationMode: 'MIXED' // Default to mixed mode
    } as any,
    create: {
      id: conversationId,
      studentId: user.id,
      botId: botId,
      transcript: transcript,
      // Campos OpenAI – cast a any
      openaiSummary: feedbackData.summary,
      openaiEvaluation: feedbackData.evaluation,
      openaiFeedback: feedbackData.feedback,
      openaiTranscript: feedbackData.transcript,
      feedbackStatus: 'completed',
      startedAt: startedAt,
      endedAt: now,
      durationSeconds: durationSeconds,
      conversationMode: 'MIXED'
    } as any
  });
  
  logger.info('Conversation feedback generated and stored successfully', {
    userId: user.id,
    conversationId: conversation.id,
    botId,
    hasSummary: !!feedbackData.summary,
    hasEvaluation: !!feedbackData.evaluation,
    hasFeedback: !!feedbackData.feedback,
    hasTranscript: !!feedbackData.transcript
  });
  
  res.json({
    success: true,
    data: {
      conversationId: conversation.id,
      summary: feedbackData.summary,
      evaluation: feedbackData.evaluation,
      feedback: feedbackData.feedback,
      transcript: feedbackData.transcript,
      feedbackStatus: 'completed'
    }
  });
});

// Get conversation feedback (if exists)
export const getConversationFeedback = asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
  const { conversationId } = req.params as any;
  const user = req.user;
  
  if (!user) {
    throw createError('User not authenticated', 401);
  }
  
  // Get conversation with OpenAI feedback
  const conversation = await prisma.conversation.findUnique({
    where: { id: conversationId },
    select: {
      id: true,
      studentId: true,
      botId: true,
      // Campos OpenAI – cast posterior
      openaiSummary: true,
      openaiEvaluation: true,
      openaiFeedback: true,
      openaiTranscript: true,
      feedbackStatus: true,
      summary: true,
      aiAnalysis: true,
      audioTranscript: true,
      bot: {
        select: {
          id: true,
          name: true,
          level: true
        }
      }
    } as any
  });
  
  if (!conversation) {
    throw createError('Conversation not found', 404);
  }
  
  // Check if user has access to this conversation
  if (user.role !== 'ADMIN' && user.role !== 'TEACHER' && (conversation as any).studentId !== user.id) {
    throw createError('Access denied to this conversation', 403);
  }
  
  const convAny = conversation as any;
  
  logger.info('Conversation feedback retrieved', {
    userId: user.id,
    conversationId,
    hasOpenAIFeedback: !!(convAny.openaiSummary || convAny.openaiFeedback),
    feedbackStatus: convAny.feedbackStatus
  });
  
  res.json({
    success: true,
    data: {
      conversationId: conversation.id,
      bot: convAny.bot,
      openaiSummary: convAny.openaiSummary,
      openaiEvaluation: convAny.openaiEvaluation,
      openaiFeedback: convAny.openaiFeedback,
      openaiTranscript: convAny.openaiTranscript,
      feedbackStatus: convAny.feedbackStatus,
      // Fallback a ElevenLabs si no hay datos OpenAI
      summary: convAny.openaiSummary || convAny.summary,
      evaluation: convAny.openaiEvaluation || convAny.aiAnalysis,
      transcript: convAny.openaiTranscript || convAny.audioTranscript
    }
  });
});
