import { Router } from 'express';
import { prisma } from '../index';
import { openaiService } from '../services/openaiService';
import { authenticateToken, AuthenticatedRequest } from '../middlewares/auth';
import { elevenLabsService } from '../services/elevenLabsService';
import { enforceUserMonthlyQuota } from '../middlewares/quota';
import { resolveEvaluationIdentifiers, attachResolvedEvaluations } from '../utils/evaluationCriteria';
import {
  getConfig,
  updateApiKey,
  testApiKey,
  getSignedUrl,
  getConversationToken,
  getSignedUrlByBotName,
  getConversationTokenByBotName,
  validateAgentId,
  generateConversationFeedback,
  webhookEvaluation,
  getConversationDetails,
  ensureConversationFeedback,
} from '../controllers/elevenLabsController';

const router = Router();

// Admin-only routes for configuration
router.get('/config', authenticateToken, (req: AuthenticatedRequest, res, next) => {
  if (req.user?.role !== 'ADMIN') {
    return res.status(403).json({
      success: false,
      message: 'Admin access required',
    });
  }
  return next();
}, getConfig);

router.put('/config', authenticateToken, (req: AuthenticatedRequest, res, next) => {
  if (req.user?.role !== 'ADMIN') {
    return res.status(403).json({
      success: false,
      message: 'Admin access required',
    });
  }
  return next();
}, updateApiKey);

router.get('/config/test', authenticateToken, (req: AuthenticatedRequest, res, next) => {
  if (req.user?.role !== 'ADMIN') {
    return res.status(403).json({
      success: false,
      message: 'Admin access required',
    });
  }
  return next();
}, testApiKey);

// Admin/Teacher routes for agent validation
router.get('/agent/:agentId/validate', authenticateToken, (req: AuthenticatedRequest, res, next) => {
  if (req.user?.role !== 'ADMIN' && req.user?.role !== 'TEACHER') {
    return res.status(403).json({
      success: false,
      message: 'Admin or teacher access required',
    });
  }
  return next();
}, validateAgentId);

// Authenticated user routes for WebSocket connections (with quota enforcement)
router.get('/agent/:agentId/signed-url', authenticateToken, enforceUserMonthlyQuota, getSignedUrl);
router.get('/agent/:agentId/conversation-token', authenticateToken, enforceUserMonthlyQuota, getConversationToken);

// Bot-name based routes (more user-friendly, with quota enforcement)
router.get('/bot/:botName/signed-url', authenticateToken, enforceUserMonthlyQuota, getSignedUrlByBotName);
router.get('/bot/:botName/conversation-token', authenticateToken, enforceUserMonthlyQuota, getConversationTokenByBotName);

// Conversation feedback generation (authenticated users)
router.post('/conversation/feedback', authenticateToken, generateConversationFeedback);

// Ensure and persist feedback if missing (authenticated) - MUST come before generic conversation route
router.post('/conversation/:conversationId/ensure-feedback', authenticateToken, ensureConversationFeedback);

// Debug route to test routing
router.get('/debug/test', (req, res) => {
  res.json({ 
    success: true, 
    message: 'ElevenLabs routes are working',
    timestamp: new Date().toISOString(),
    path: req.path,
    method: req.method
  });
});

// Temporary route to pull conversation details directly from ElevenLabs (protected by secret header)
router.get('/conversation/:conversationId', getConversationDetails);

// Direct pull by external ElevenLabs conversation id (admin/teacher). This does NOT persist.
router.get('/external/conversation/:externalId', authenticateToken, async (req: AuthenticatedRequest, res) => {
  try {
    const { externalId } = req.params as any;
    const conversationId = (req.query?.conversationId as string) || '';

    // Admin/Teacher: always allowed
    const isElevated = req.user?.role === 'ADMIN' || req.user?.role === 'TEACHER';
    if (!isElevated) {
      // Students: allow only if the externalId belongs to one of their conversations
      try {
        const conv = conversationId
          ? await prisma.conversation.findUnique({ where: { id: conversationId } })
          : await prisma.conversation.findFirst({ where: { elevenLabsConversationId: externalId } });
        if (!conv || conv.studentId !== req.user?.id || (conv.elevenLabsConversationId && conv.elevenLabsConversationId !== externalId)) {
          return res.status(403).json({ success: false, message: 'Not allowed' });
        }
      } catch (authErr: any) {
        return res.status(403).json({ success: false, message: 'Not allowed' });
      }
    }

    const data = await elevenLabsService.getConversationDetails(externalId);

    if (conversationId) {
      const previewConv = await prisma.conversation.findUnique({
        where: { id: conversationId },
        select: { bot: { select: { agentId: true } } }
      });
      await attachResolvedEvaluations(data, previewConv?.bot?.agentId);
    }

    // F4: translate the live-preview transcript summary too — without this, a
    // conversation whose analysis wasn't ready yet at auto-ingest time (still
    // shows the DB placeholder) falls back to this endpoint and would otherwise
    // display ElevenLabs' raw, untranslated summary.
    const { analysis: translatedPreviewAnalysis } = await openaiService.translateSummaryFields(data?.analysis, '');
    if (data) data.analysis = translatedPreviewAnalysis;

    res.json({ success: true, data });
  } catch (e: any) {
    res.status(500).json({ success: false, message: e.message });
  }
});

// External conversation by our conversation id (no persistence): resolves external id best-effort and fetches
router.get('/external/conversation/by-conversation/:conversationId', authenticateToken, async (req: AuthenticatedRequest, res) => {
  try {
    const { conversationId } = req.params as any;
    const user = req.user;
    const conversation = await prisma.conversation.findUnique({ where: { id: conversationId } });
    if (!conversation) return res.status(404).json({ success: false, message: 'Conversation not found' });
    // Students can view their own conversation feedback; admins/teachers can view any
    if (user?.role === 'STUDENT' && user.id !== conversation.studentId) {
      return res.status(403).json({ success: false, message: 'Not allowed' })
    }

    let externalId = conversation.elevenLabsConversationId || '';
    if (!externalId && conversation.botId) {
      try {
        const bot = await prisma.bot.findUnique({ where: { id: conversation.botId }, select: { agentId: true } });
        if (bot?.agentId) {
          const list = await elevenLabsService.listConversationsByAgent(bot.agentId, 50);
          const startedAt = conversation.startedAt?.getTime?.() ? conversation.startedAt.getTime() : new Date(conversation.startedAt as any).getTime();
          const endedAt = conversation.endedAt ? (conversation.endedAt as any as Date).getTime?.() ? (conversation.endedAt as any as Date).getTime() : new Date(conversation.endedAt as any).getTime() : undefined;
          let best: any = null;
          let bestScore = Number.POSITIVE_INFINITY;
          for (const c of list) {
            const ts = (c.started_at || c.startedAt || c.created_at || c.createdAt);
            const t = ts ? new Date(ts).getTime() : 0;
            const score = Math.abs(t - (endedAt ?? startedAt));
            if (t && score < bestScore) { bestScore = score; best = c; }
          }
          if (best?.id) {
            externalId = best.id;
          }
        }
      } catch (err) {
        // best-effort only
      }
    }

    if (!externalId) return res.json({ success: true, data: {} });

    const data = await elevenLabsService.getConversationDetails(externalId);

    if (conversation.botId) {
      const previewBot = await prisma.bot.findUnique({ where: { id: conversation.botId }, select: { agentId: true } });
      await attachResolvedEvaluations(data, previewBot?.agentId);
    }

    // F4: translate the live-preview transcript summary too (see sibling route above).
    const { analysis: translatedPreviewAnalysis } = await openaiService.translateSummaryFields(data?.analysis, '');
    if (data) data.analysis = translatedPreviewAnalysis;

    res.json({ success: true, data });
  } catch (e: any) {
    res.status(500).json({ success: false, message: e.message });
  }
});

// Ingest by external ElevenLabs id into one of our conversations (owner/admin/teacher)
router.post('/external/ingest/:externalId/:conversationId', authenticateToken, async (req: AuthenticatedRequest, res) => {
  try {
    const { externalId, conversationId } = req.params as any;
    // Authorization: allow owner (student) or teacher/admin
    const conv = await prisma.conversation.findUnique({ where: { id: conversationId } });
    if (!conv) return res.status(404).json({ success: false, message: 'Conversation not found' });
    const isOwner = req.user?.id && conv.studentId === req.user.id;
    const isElevated = req.user?.role === 'ADMIN' || req.user?.role === 'TEACHER';
    if (!isOwner && !isElevated) {
      return res.status(403).json({ success: false, message: 'Not allowed' });
    }
    const details: any = await elevenLabsService.getConversationDetails(externalId);

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

    if (evaluations.length) {
      const evalBot = await prisma.bot.findUnique({ where: { id: conv.botId }, select: { agentId: true } });
      evaluations = await resolveEvaluationIdentifiers(evalBot?.agentId, evaluations);
    }

    // Prefer call summary
    const summary = (
      details?.call_summary_title
      || details?.data?.call_summary_title
      || details?.summary
      || details?.overview?.summary
      || 'Evaluation received'
    );

    // Transcript pretty text
    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')
      : '';

    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: details?.analysis,
      evaluations,
      metadata: details?.metadata,
      conversation_initiation_client_data: details?.conversation_initiation_client_data,
      raw_payload: details
    }, null, 2);

    // Persist ElevenLabs data first
    const updated = await prisma.conversation.update({ 
      where: { id: conversationId }, 
      data: { 
        summary, 
        aiAnalysis, 
        elevenLabsConversationId: externalId,
        audioTranscript: transcriptText || undefined
      } 
    });

    // Auto-generate OpenAI feedback using the ingested transcript (if configured)
    try {
      // OpenAI feedback generation is now handled by conversationService.ts
      // to avoid duplicate calls. This endpoint only handles ElevenLabs data.
    } catch (oaErr) {
      // No romper el flujo de ingesta si OpenAI falla; solo reportar
      console.warn('OpenAI auto-feedback failed:', oaErr);
    }

    res.json({ success: true, data: updated });
  } catch (e: any) {
    res.status(500).json({ success: false, message: e.message });
  }
});

// Webhook endpoint (no auth) to receive evaluation results
router.post('/webhooks/evaluation', webhookEvaluation);

export default router;

