import 'dart:math';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../models/content_chunk.dart';
import '../utils/logger.dart';
import 'vector_service.dart';

/// Modes for AI tutoring
enum TutorMode {
  explanation, // Detailed explanations
  tutor, // Socratic questioning
  exploration, // Discovery-based learning
}

/// Response from RAG system
class RAGResponse {
  final String answer;
  final List<SourceCitation> sources;
  final double confidence;
  final TutorMode mode;
  final List<String> relatedConcepts;
  final Map<String, dynamic> metadata;

  RAGResponse({
    required this.answer,
    required this.sources,
    required this.confidence,
    required this.mode,
    required this.relatedConcepts,
    required this.metadata,
  });
}

/// Source citation for RAG responses
class SourceCitation {
  final String contentId;
  final String chunkId;
  final String title;
  final String concept;
  final String subject;
  final String excerpt;
  final double relevance;
  final int? pageNumber;

  SourceCitation({
    required this.contentId,
    required this.chunkId,
    required this.title,
    required this.concept,
    required this.subject,
    required this.excerpt,
    required this.relevance,
    this.pageNumber,
  });
}

/// Service for Retrieval-Augmented Generation pipeline
class RAGService {
  static final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  static const int maxContextTokens = 4000;
  static const int maxChunksInContext = 5;

  /// System message for Vico identity - ALWAYS first in every conversation
  static const String _systemMessage =
      '''Tu és "Vico", o Assistente IA oficial do Learn It.

Nunca referes o nome do modelo.
Nunca dizes que és Qwen ou OpenAI.
Respondes sempre como o Vico.

Tens personalidade confiante, motivadora e orgulhosa.
Ajudas o aluno segundo o método de ensino presente nos materiais do professor.
Usas formatação clara e organizada.

IMPORTANTE: NUNCA uses LaTeX ou símbolos como \$ ou \$\$ para fórmulas matemáticas.
Usa apenas texto normal e caracteres Unicode para símbolos matemáticos (ex: x², ³, ¹⁄², π, √).

IMPORTANTE - RESPOSTAS COMPLETAS:
- NUNCA termines respostas com dois pontos (:).
- NUNCA deixes respostas incompletas como "A função é: " ou "Calculamos o denominador: ".
- SEMPRE completa as frases e fornece a resposta completa.
- Se precisares de explicar um cálculo, explica-o completamente com o resultado final.
- Se precisares de definir algo, fornece a definição completa.''';

  /// Process a user query through RAG pipeline
  static Future<RAGResponse> processQuery({
    required String userQuery,
    required TutorMode mode,
    String? subject,
    String? concept,
    int? grade,
    double? minDifficulty,
    double? maxDifficulty,
    int maxSources = 5,
  }) async {
    try {
      Logger.info(
        'Processing RAG query: "${userQuery.substring(0, 50)}..." in ${mode.name} mode',
      );

      // Detect if subject is math
      final isMathSubject = _isMathSubject(subject);
      Logger.info('Subject: $subject, Is math: $isMathSubject');

      // 1. Generate embedding for user query
      final queryEmbedding = VectorService.generateEmbedding(userQuery);

      // 2. Retrieve relevant content chunks
      final relevantChunks = await VectorService.searchSimilar(
        queryEmbedding: queryEmbedding,
        subject: subject,
        concept: concept,
        grade: grade,
        minDifficulty: minDifficulty,
        maxDifficulty: maxDifficulty,
        k: maxSources + 2, // Get extra for filtering
        threshold: 0.3,
      );

      if (relevantChunks.isEmpty) {
        Logger.warning('No relevant content found for query');
        return _createNoContentResponse(userQuery, mode);
      }

      // 3. Build context window with math-specific filtering
      final context = _buildContextWindow(
        relevantChunks,
        userQuery,
        mode,
        isMathSubject: isMathSubject,
      );

      // 4. Generate response (this will be handled by RAGAIService)
      final response = await _generateResponse(
        query: userQuery,
        context: context,
        mode: mode,
        sources: relevantChunks.take(maxSources).toList(),
      );

      Logger.info(
        'RAG response generated with confidence: ${response.confidence}',
      );
      return response;
    } catch (e) {
      Logger.error('Error processing RAG query: $e');
      return _createErrorResponse(userQuery, mode, e.toString());
    }
  }

  /// Build context window from relevant chunks
  static String _buildContextWindow(
    List<ContentChunk> chunks,
    String userQuery,
    TutorMode mode, {
    bool isMathSubject = false,
  }) {
    try {
      final contextBuilder = StringBuffer();

      // Add context header
      contextBuilder.writeln('=== CONTEÚDO EDUCACIONAL RELEVANTE ===\n');

      // Sort chunks by relevance and take top chunks
      final sortedChunks = chunks.take(maxChunksInContext).toList();

      for (int i = 0; i < sortedChunks.length; i++) {
        final chunk = sortedChunks[i];
        var chunkText = chunk.text;

        // Filter table data for math subjects
        if (isMathSubject) {
          chunkText = _filterTableData(chunkText);
        }

        contextBuilder.writeln('--- Fonte ${i + 1} ---');
        contextBuilder.writeln('Disciplina: ${chunk.subject}');
        contextBuilder.writeln('Conceito: ${chunk.concept}');
        if (chunk.subConcept != null) {
          contextBuilder.writeln('Subconceito: ${chunk.subConcept}');
        }
        contextBuilder.writeln('Unidade: ${chunk.unit}');
        contextBuilder.writeln('Dificuldade: ${chunk.difficulty}');
        if (chunk.pageNumber != null) {
          contextBuilder.writeln('Página: ${chunk.pageNumber}');
        }
        contextBuilder.writeln('\nConteúdo:\n$chunkText\n');
      }

      // Add mode-specific instructions
      contextBuilder.writeln('\n=== INSTRUÇÕES DE TUTORIA ===');
      contextBuilder.writeln('Modo: ${_getModeInstructions(mode)}');

      // Add math-specific instructions if applicable
      if (isMathSubject) {
        contextBuilder.writeln('\n=== INSTRUÇÕES PARA MATEMÁTICA ===');
        contextBuilder.writeln('Para notações matemáticas:');
        contextBuilder.writeln(
          r'- NUNCA use LaTeX ou símbolos como $ ou $$ para fórmulas',
        );
        contextBuilder.writeln(
          '- Use apenas texto normal e caracteres Unicode (ex: x², ³, ¹⁄², π, √)',
        );
        contextBuilder.writeln(
          '- Preserve a notação matemática original quando possível',
        );
        contextBuilder.writeln('- Explique passo a passo os cálculos');
        contextBuilder.writeln('- Use exemplos numéricos concretos');
      }

      contextBuilder.writeln('Pergunta do Aluno: $userQuery\n');

      final contextText = contextBuilder.toString();

      // Check context length and truncate if necessary
      if (contextText.length > maxContextTokens) {
        Logger.warning('Context too long, truncating');
        return contextText.substring(0, maxContextTokens - 100) +
            '...[truncated]';
      }

      return contextText;
    } catch (e) {
      Logger.error('Error building context window: $e');
      return 'Erro ao construir contexto';
    }
  }

  /// Get mode-specific instructions
  static String _getModeInstructions(TutorMode mode) {
    switch (mode) {
      case TutorMode.explanation:
        return 'Forneça explicações detalhadas e claras baseadas apenas no conteúdo fornecido. Use exemplos do material e estruture a resposta de forma lógica.';
      case TutorMode.tutor:
        return 'Use o método socrático - faça perguntas que guiem o aluno a descobrir a resposta. Baseie-se apenas no conteúdo fornecido e incentive o pensamento crítico.';
      case TutorMode.exploration:
        return 'Ajude o aluno a explorar o conceito através de descoberta. Conecte com ideias relacionadas e sugira investigações baseadas no conteúdo.';
    }
  }

  /// Generate response (placeholder - will be implemented in RAGAIService)
  static Future<RAGResponse> _generateResponse({
    required String query,
    required String context,
    required TutorMode mode,
    required List<ContentChunk> sources,
  }) async {
    try {
      // Create source citations
      final citations = sources
          .map(
            (chunk) => SourceCitation(
              contentId: chunk.contentId,
              chunkId: chunk.id,
              title: chunk.sourceDocument,
              concept: chunk.concept,
              subject: chunk.subject,
              excerpt: _getExcerpt(chunk.text),
              relevance: _calculateRelevance(query, chunk.text),
              pageNumber: chunk.pageNumber,
            ),
          )
          .toList();

      // Call Ollama API
      final url = Uri.parse('http://89.114.196.110:11434/api/chat');

      // Build prompt with context
      final prompt = _buildPrompt(query, context, mode);

      final response = await http
          .post(
            url,
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode({
              'model': 'qwen3-coder:30b',
              'messages': [
                {'role': 'system', 'content': _systemMessage},
                {'role': 'user', 'content': prompt},
              ],
              'stream': false,
            }),
          )
          .timeout(const Duration(seconds: 60));

      if (response.statusCode == 200) {
        final responseData = jsonDecode(response.body);
        var answer = responseData['message']['content'] as String;

        // Post-process to remove LaTeX symbols
        answer = _removeLaTeXSymbols(answer);

        final confidence = _calculateConfidence(sources);
        final relatedConcepts = _extractRelatedConcepts(sources);

        return RAGResponse(
          answer: answer,
          sources: citations,
          confidence: confidence,
          mode: mode,
          relatedConcepts: relatedConcepts,
          metadata: {
            'queryLength': query.length,
            'contextLength': context.length,
            'sourceCount': sources.length,
            'processingTime': DateTime.now().millisecondsSinceEpoch,
            'apiResponse': true,
          },
        );
      } else {
        Logger.error('Ollama API error: ${response.statusCode}');
        // Fallback to mock response
        final answer = _generateMockAnswer(query, context, mode);
        final confidence = _calculateConfidence(sources);
        final relatedConcepts = _extractRelatedConcepts(sources);

        return RAGResponse(
          answer: answer,
          sources: citations,
          confidence: confidence,
          mode: mode,
          relatedConcepts: relatedConcepts,
          metadata: {
            'queryLength': query.length,
            'contextLength': context.length,
            'sourceCount': sources.length,
            'processingTime': DateTime.now().millisecondsSinceEpoch,
            'apiResponse': false,
            'apiError': response.statusCode,
          },
        );
      }
    } catch (e) {
      Logger.error('Error calling Ollama API: $e');
      // Fallback to mock response
      final answer = _generateMockAnswer(query, context, mode);
      final confidence = _calculateConfidence(sources);
      final relatedConcepts = _extractRelatedConcepts(sources);

      return RAGResponse(
        answer: answer,
        sources: sources
            .map(
              (chunk) => SourceCitation(
                contentId: chunk.contentId,
                chunkId: chunk.id,
                title: chunk.sourceDocument,
                concept: chunk.concept,
                subject: chunk.subject,
                excerpt: _getExcerpt(chunk.text),
                relevance: _calculateRelevance(query, chunk.text),
                pageNumber: chunk.pageNumber,
              ),
            )
            .toList(),
        confidence: confidence,
        mode: mode,
        relatedConcepts: relatedConcepts,
        metadata: {
          'queryLength': query.length,
          'contextLength': context.length,
          'sourceCount': sources.length,
          'processingTime': DateTime.now().millisecondsSinceEpoch,
          'apiResponse': false,
          'apiError': e.toString(),
        },
      );
    }
  }

  /// Get excerpt from text
  static String _getExcerpt(String text, {int maxLength = 200}) {
    if (text.length <= maxLength) return text;
    return text.substring(0, maxLength - 3) + '...';
  }

  /// Build prompt for AI model with educational context
  static String _buildPrompt(String query, String context, TutorMode mode) {
    final modeInstructions = _getModeInstructions(mode);

    return '''$modeInstructions

=== CONTEÚDO EDUCACIONAL RELEVANTE ===
$context

=== PERGUNTA DO ALUNO ===
$query

=== INSTRUÇÕES ===
- Baseie sua resposta APENAS no conteúdo educacional fornecido acima
- Se o conteúdo não for suficiente para responder, explique o que está disponível
- Use linguagem clara e educacional
- Adapte a resposta ao nível do aluno
- Forneça exemplos quando possível
- Seja conciso mas completo
- NUNCA use LaTeX ou símbolos como \$ ou \$\$ para fórmulas
- Use apenas texto normal e caracteres Unicode para símbolos matemáticos''';
  }

  /// Calculate relevance score
  static double _calculateRelevance(String query, String text) {
    final queryWords = query.toLowerCase().split(' ');
    final textLower = text.toLowerCase();

    int matches = 0;
    for (final word in queryWords) {
      if (word.length > 2 && textLower.contains(word)) {
        matches++;
      }
    }

    return matches / queryWords.length;
  }

  /// Generate mock answer (placeholder)
  static String _generateMockAnswer(
    String query,
    String context,
    TutorMode mode,
  ) {
    switch (mode) {
      case TutorMode.explanation:
        return 'Baseado no conteúdo fornecido, posso explicar que $query. Esta abordagem é fundamentada nos conceitos apresentados no material educacional. A resposta detalhada envolve os princípios teóricos e práticos descritos nas fontes relevantes.';
      case TutorMode.tutor:
        return 'Para entender $query, vamos explorar juntos. O que você já sabe sobre este conceito? Baseado no material, podemos investigar os aspectos fundamentais passo a passo.';
      case TutorMode.exploration:
        return 'Vamos descobrir mais sobre $query! O conteúdo sugere várias abordagens interessantes. Que aspecto gostaria de explorar primeiro? Podemos investigar as conexões entre os diferentes conceitos apresentados.';
    }
  }

  /// Calculate confidence score
  static double _calculateConfidence(List<ContentChunk> sources) {
    if (sources.isEmpty) return 0.0;

    // Base confidence on number and quality of sources
    double confidence = min(sources.length * 0.2, 0.8);

    // Boost confidence if sources are highly relevant
    final avgRelevance =
        sources.map((s) => s.difficulty).reduce((a, b) => a + b) /
        sources.length;
    confidence += avgRelevance * 0.2;

    return min(confidence, 1.0);
  }

  /// Extract related concepts
  static List<String> _extractRelatedConcepts(List<ContentChunk> sources) {
    final concepts = <String>{};

    for (final source in sources) {
      concepts.add(source.concept);
      if (source.subConcept != null) {
        concepts.add(source.subConcept!);
      }
    }

    return concepts.toList()..sort();
  }

  /// Detect if subject is math-related
  static bool _isMathSubject(String? subject) {
    if (subject == null) return false;
    final lowerSubject = subject.toLowerCase();

    final mathKeywords = [
      'matemática',
      'math',
      'matematica',
      'álgebra',
      'algebra',
      'geometria',
      'cálculo',
      'calculo',
      'estatística',
      'estatistica',
      'funções',
      'funcoes',
      'equações',
      'equacoes',
      'números',
      'numeros',
    ];

    return mathKeywords.any((keyword) => lowerSubject.contains(keyword));
  }

  /// Filter out table data from text (for math subjects)
  /// Removes lines that look like tabular data with multiple numbers
  static String _filterTableData(String text) {
    final lines = text.split('\n');
    final filtered = <String>[];

    for (final line in lines) {
      final trimmed = line.trim();

      // Skip lines that look like table data
      // Pattern: multiple numbers separated by spaces/tabs
      final numberPattern = RegExp(r'\d+\s+\d+');
      final matches = numberPattern.allMatches(trimmed);

      // If a line has 2+ number pairs separated by spaces, it's likely table data
      if (matches.length >= 2) {
        continue;
      }

      // Skip lines with specific date patterns (table data)
      if (RegExp(r'\d{1,2}/\d{1,2}/\d{4}').hasMatch(trimmed) &&
          RegExp(r'\d+').allMatches(trimmed).length > 2) {
        continue;
      }

      // Keep the line
      filtered.add(line);
    }

    return filtered.join('\n');
  }

  /// Remove LaTeX symbols from AI response
  static String _removeLaTeXSymbols(String text) {
    // Remove patterns like $...$ and $$...$$
    var cleaned = text.replaceAll(RegExp(r'\$\$[^$]+\$\$'), '');
    cleaned = cleaned.replaceAll(RegExp(r'\$[^$]+\$'), '');

    // Also remove standalone $ symbols
    cleaned = cleaned.replaceAll(r'$', r'\$');

    return cleaned;
  }

  /// Create response for no content found
  static RAGResponse _createNoContentResponse(String query, TutorMode mode) {
    return RAGResponse(
      answer:
          'Desculpe, não encontrei conteúdo relevante para responder à sua pergunta sobre "$query". Tente reformular a pergunta ou verifique se o material sobre este tópico está disponível.',
      sources: [],
      confidence: 0.0,
      mode: mode,
      relatedConcepts: [],
      metadata: {'error': 'no_content_found'},
    );
  }

  /// Create error response
  static RAGResponse _createErrorResponse(
    String query,
    TutorMode mode,
    String error,
  ) {
    return RAGResponse(
      answer:
          'Ocorreu um erro ao processar sua pergunta. Por favor, tente novamente mais tarde.',
      sources: [],
      confidence: 0.0,
      mode: mode,
      relatedConcepts: [],
      metadata: {'error': error},
    );
  }

  /// Get conversation history for context
  static Future<List<Map<String, dynamic>>> getConversationHistory({
    required String userId,
    int limit = 10,
  }) async {
    try {
      Logger.info('Getting conversation history for user: $userId');

      final querySnapshot = await _firestore
          .collection('conversations')
          .where('userId', isEqualTo: userId)
          .orderBy('createdAt', descending: true)
          .limit(limit)
          .get();

      return querySnapshot.docs
          .map((doc) => {'id': doc.id, ...doc.data() as Map<String, dynamic>})
          .toList();
    } catch (e) {
      Logger.error('Error getting conversation history: $e');
      return [];
    }
  }

  /// Save conversation to Firestore
  static Future<String> saveConversation({
    required String userId,
    required String query,
    required RAGResponse response,
  }) async {
    try {
      Logger.info('Saving conversation for user: $userId');

      final conversationData = {
        'userId': userId,
        'query': query,
        'answer': response.answer,
        'mode': response.mode.name,
        'confidence': response.confidence,
        'sources': response.sources
            .map(
              (s) => {
                'contentId': s.contentId,
                'chunkId': s.chunkId,
                'title': s.title,
                'concept': s.concept,
                'subject': s.subject,
                'excerpt': s.excerpt,
                'relevance': s.relevance,
                'pageNumber': s.pageNumber,
              },
            )
            .toList(),
        'relatedConcepts': response.relatedConcepts,
        'metadata': response.metadata,
        'createdAt': FieldValue.serverTimestamp(),
      };

      final docRef = await _firestore
          .collection('conversations')
          .add(conversationData);
      final conversationId = docRef.id;

      Logger.info('Conversation saved: $conversationId');
      return conversationId;
    } catch (e) {
      Logger.error('Error saving conversation: $e');
      throw Exception('Failed to save conversation: $e');
    }
  }

  /// Get popular concepts
  static Future<List<Map<String, dynamic>>> getPopularConcepts({
    int limit = 20,
  }) async {
    try {
      Logger.info('Getting popular concepts');

      final querySnapshot = await _firestore
          .collection('contentChunks')
          .where('isActive', isEqualTo: true)
          .limit(100)
          .get();

      final conceptCounts = <String, int>{};

      for (final doc in querySnapshot.docs) {
        final data = doc.data();
        final concept = data['concept'] as String? ?? 'Unknown';
        conceptCounts[concept] = (conceptCounts[concept] ?? 0) + 1;
      }

      final sortedConcepts = conceptCounts.entries.toList()
        ..sort((a, b) => b.value.compareTo(a.value));

      return sortedConcepts
          .take(limit)
          .map((entry) => {'concept': entry.key, 'count': entry.value})
          .toList();
    } catch (e) {
      Logger.error('Error getting popular concepts: $e');
      return [];
    }
  }
}
