import 'dart:async';
import 'dart:io';
import 'dart:collection';
import 'dart:math' as math;

import 'package:country_codes/country_codes.dart';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:path_provider/path_provider.dart';
import 'package:search_engine/screens/content.dart';
import 'package:search_engine/widgets/base.dart';
import 'package:skeletonizer/skeletonizer.dart';
import '../database.dart';
import 'package:drift/drift.dart' as drift;
import 'package:search_engine/utils.dart' as utils;
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'
    as nav;
import 'package:flutter/services.dart';
import 'package:search_engine/services/mimir_service.dart';

// Clase auxiliar para representar una coincidencia
class _Match {
  final int start;
  final int end;
  final String text;

  _Match({required this.start, required this.end, required this.text});
}

// Clase auxiliar para representar un fragmento de texto
class _TextFragment {
  final String beforeMatch;
  final String matchText;
  final String afterMatch;

  _TextFragment({
    required this.beforeMatch,
    required this.matchText,
    required this.afterMatch,
  });
}

// Clase para el caché de búsqueda
class SearchCache {
  // LRU Cache con capacidad máxima
  final int _maxSize;
  final LinkedHashMap<String, List<Draft>> _cache = LinkedHashMap();
  final Map<String, int> _totalResultsCache = {};
  // Almacena todos los resultados de una búsqueda
  final Map<String, List<Draft>> _allResultsCache = {};
  // Almacena los resultados de Mimir (IDs y snippets)
  final Map<String, List<Map<String, String>>> _mimirResultsCache = {};
  // Almacena todos los resultados de Mimir para una consulta
  final Map<String, List<Map<String, String>>> _allMimirResultsCache = {};

  SearchCache({int maxSize = 10}) : _maxSize = maxSize;

  // Obtener resultados del caché
  List<Draft>? get(String query) {
    final normalizedQuery = query.trim().toLowerCase();
    if (!_cache.containsKey(normalizedQuery)) return null;

    // Mover el elemento al final (más reciente)
    final results = _cache.remove(normalizedQuery);
    _cache[normalizedQuery] = results!;
    return results;
  }

  // Guardar resultados en el caché
  void put(String query, List<Draft> results) {
    final normalizedQuery = query.trim().toLowerCase();

    // Si el caché está lleno, eliminar el elemento más antiguo
    if (_cache.length >= _maxSize) {
      final oldestKey = _cache.keys.first;
      _cache.remove(oldestKey);
      // También eliminar del caché de todos los resultados
      _allResultsCache.remove(oldestKey);
      _totalResultsCache.remove(oldestKey);
      _mimirResultsCache.remove(oldestKey);
      _allMimirResultsCache.remove(oldestKey);
    }

    _cache[normalizedQuery] = results;
  }

  // Limpiar el caché
  void clear() {
    _cache.clear();
    _totalResultsCache.clear();
    _allResultsCache.clear();
    _mimirResultsCache.clear();
    _allMimirResultsCache.clear();
  }

  // Obtener el total de resultados para una consulta
  int? getTotalResults(String query) {
    final normalizedQuery = query.trim().toLowerCase();
    return _totalResultsCache[normalizedQuery];
  }

  // Guardar el total de resultados para una consulta
  void setTotalResults(String query, int total) {
    final normalizedQuery = query.trim().toLowerCase();
    _totalResultsCache[normalizedQuery] = total;
  }

  // Guardar todos los resultados de una búsqueda
  void putAllResults(String query, List<Draft> results) {
    final normalizedQuery = query.trim().toLowerCase();
    _allResultsCache[normalizedQuery] = results;
  }

  // Obtener todos los resultados de una búsqueda
  List<Draft>? getAllResults(String query) {
    final normalizedQuery = query.trim().toLowerCase();
    return _allResultsCache[normalizedQuery];
  }

  // Guardar los resultados de Mimir (IDs y snippets)
  void setMimirIds(String query, List<Map<String, String>> mimirResults) {
    final normalizedQuery = query.trim().toLowerCase();
    _mimirResultsCache[normalizedQuery] = mimirResults;
    // También actualizar el total de resultados
    _totalResultsCache[normalizedQuery] = mimirResults.length;
  }

  // Obtener los resultados de Mimir (IDs y snippets)
  List<Map<String, String>>? getMimirIds(String query) {
    final normalizedQuery = query.trim().toLowerCase();
    return _mimirResultsCache[normalizedQuery];
  }

  // Guardar todos los resultados de Mimir para una consulta
  void setAllMimirResults(String query, List<Map<String, String>> results) {
    final normalizedQuery = query.trim().toLowerCase();
    _allMimirResultsCache[normalizedQuery] = results;
  }

  // Obtener todos los resultados de Mimir para una consulta
  List<Map<String, String>>? getAllMimirResults(String query) {
    final normalizedQuery = query.trim().toLowerCase();
    return _allMimirResultsCache[normalizedQuery];
  }
}

class SearchPage extends StatefulWidget {
  final String? searchTerm;

  const SearchPage({super.key, this.searchTerm});

  @override
  // ignore: library_private_types_in_public_api
  _SearchPageState createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> with TickerProviderStateMixin {
  late TextEditingController _searchController;
  late ScrollController _scrollController;
  late AppDatabase database;
  late Directory appDirectory;
  int resultCount = 0;
  int totalResults = 0;
  bool isLoading = false;
  // Nuevas variables para separar las fases de carga
  bool mimirLoading = false; // Para la fase de obtención de IDs de Mimir
  bool resultsLoading = false; // Para la fase de carga de detalles
  Timer? _debounce;
  final _baseUrl = dotenv.env['BASE_URL'];
  final _token = dotenv.env['TOKEN'];
  bool _isSearching = false;
  late AnimationController _animationController;
  late Animation<double> _fadeAnimation;

  // Controlador para la animación de pulso del indicador de búsqueda
  late AnimationController _pulseController;
  late Animation<double> _pulseAnimation;

  // Controlador de paginación
  final PagingController<int, Draft> _pagingController = PagingController(
    firstPageKey: 1,
  );

  // Tamaño de página para la carga incremental
  static const _pageSize = 10;

  // Consulta actual
  String _currentQuery = '';

  // Almacena todos los resultados de la búsqueda actual
  List<Draft>? _allSearchResults;

  // Instancia del caché de búsqueda
  final SearchCache _searchCache = SearchCache(maxSize: 20);

  final _mimirService = MimirService();

  // Caché para nombres de países
  final Map<String, String> _countryNameCache = {};

  @override
  void initState() {
    super.initState();
    _searchController = TextEditingController(text: widget.searchTerm);
    _scrollController = ScrollController();
    _initAppDirectory();
    database = AppDatabase();
    _mimirService.initialize();

    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );

    _fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: _animationController,
        curve: Curves.easeIn,
      ),
    );

    // Inicializar el controlador de la animación de pulso
    _pulseController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1500),
    )..repeat(reverse: true);

    _pulseAnimation = Tween<double>(begin: 0.8, end: 1.2).animate(
      CurvedAnimation(
        parent: _pulseController,
        curve: Curves.easeInOut,
      ),
    );

    // Configurar el listener para la paginación
    _pagingController.addPageRequestListener((pageKey) {
      if (_currentQuery.isNotEmpty) {
        _fetchPage(pageKey);
      }
    });

    if (_searchController.text.isNotEmpty) {
      Timer(const Duration(milliseconds: 100), () {
        _onSearch(_searchController.text);
      });
    }
  }

  @override
  void dispose() {
    _pagingController.dispose();
    _animationController.dispose();
    _pulseController.dispose();
    _scrollController.dispose();
    database.close();
    _debounce?.cancel();
    _searchController.dispose();
    super.dispose();
  }

  void _onSearch(String value) {
    if (_searchController.text.trim().isNotEmpty) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          setState(() {
            _isSearching = true;
            _currentQuery = _searchController.text;
            totalResults = 0;
            isLoading = true;
            mimirLoading = true;
            resultsLoading = true;
            _allSearchResults = null;
          });

          // Limpiar caché de resaltado para la nueva búsqueda
          _highlightedSpansCache.clear();

          // Refrescar el controlador de paginación
          _pagingController.refresh();
          _animationController.forward();
        }
      });
    } else {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          setState(() {
            _isSearching = false;
            _currentQuery = '';
            totalResults = 0;
            isLoading = false;
            mimirLoading = false;
            resultsLoading = false;
            _allSearchResults = null;
          });

          // Limpiar caché de resaltado
          _highlightedSpansCache.clear();

          // Limpiar y refrescar el controlador de paginación
          _pagingController.itemList?.clear();
          _pagingController.refresh();

          if (_animationController.isAnimating) {
            _animationController.stop();
          }
          if (_animationController.value > 0) {
            _animationController.reverse();
          }
        }
      });
    }
  }

  void _clearSearch() {
    _searchController.clear();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        setState(() {
          _isSearching = false;
          _currentQuery = '';
          isLoading = false;
          mimirLoading = false;
          resultsLoading = false;
          _allSearchResults = null;
        });

        // Limpiar caché de resaltado
        _highlightedSpansCache.clear();

        // Refrescar el controlador de paginación
        _pagingController.refresh();

        if (_animationController.isAnimating) {
          _animationController.stop();
        }
        if (_animationController.value > 0) {
          _animationController.reverse();
        }
      }
    });
  }

  // Nueva función para búsqueda completa en Mimir
  static Future<Map<String, dynamic>> _searchMimirFullIsolate(
      List<dynamic> params) async {
    final query = params[0] as String;
    final locale = params[1] as String;
    final token = params[2] as RootIsolateToken;

    BackgroundIsolateBinaryMessenger.ensureInitialized(token);
    final mimirService = MimirService();
    final response = await mimirService.search(
      query,
      locale,
      limit: 10000,
      offset: 0,
    );
    return response;
  }

  Future<void> _fetchPage(int pageKey) async {
    if (!mounted) return;

    try {
      // Check if results are in cache
      final cachedResults = _searchCache.get('${_currentQuery}_page_$pageKey');

      if (cachedResults != null) {
        final isLastPage = cachedResults.length < _pageSize;
        if (isLastPage) {
          _pagingController.appendLastPage(cachedResults);
        } else {
          _pagingController.appendPage(cachedResults, pageKey + 1);
        }
        return;
      }

      // Calculate offset and limit for pagination
      final offset = (pageKey - 1) * _pageSize;
      final limit = _pageSize;

      // Show loading state immediately
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          setState(() {
            isLoading = true;
            mimirLoading =
                pageKey == 1; // Only show Mimir loading for first page
            resultsLoading = true;
          });
        }
      });

      // Get the RootIsolateToken before starting the search
      final token = RootIsolateToken.instance;
      if (token == null) {
        throw Exception('RootIsolateToken is not initialized');
      }

      // For the first page, perform the complete Mimir search
      List<Map<String, String>> mimirResults;
      int total = 0;

      if (pageKey == 1) {
        // For the first page, do a complete search in Mimir
        // Using Future to avoid blocking the UI
        final searchResponse = await _mimirService.search(
          _currentQuery,
          context.locale.toString(),
          limit: 500, // Increased limit to show more results
          offset: 0,
        );

        // Obtener resultados únicos por ID de documento
        final allMimirResults =
            (searchResponse['results'] as List<Map<String, String>>)
                .fold<Map<String, Map<String, String>>>({}, (map, result) {
                  if (!map.containsKey(result['id'])) {
                    map[result['id']!] = result;
                  }
                  return map;
                })
                .values
                .toList();

        // Use the total from the search response, not just the length of the filtered results
        total = searchResponse['total']
            as int; // This will get the actual total from the search engine

        // Update total results immediately, after the frame
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            setState(() {
              totalResults = total;
              mimirLoading = false;
            });
          }
        });

        // Store all Mimir results for future use
        _searchCache.setAllMimirResults(_currentQuery, allMimirResults);
        _searchCache.setTotalResults(_currentQuery, total);

        // Get only the results for the first page
        mimirResults =
            allMimirResults.sublist(0, limit.clamp(0, allMimirResults.length));

        // If there are no results, finish here
        if (mimirResults.isEmpty) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              setState(() {
                isLoading = false;
                resultsLoading = false;
              });
              _pagingController.appendLastPage([]);
            }
          });
          return;
        }

        // Use compute to process results in the background
        final List<String> ids =
            mimirResults.map((result) => result['id']!).toList();
        final String locale = context.locale.toString();

        // Prepare parameters for computation
        final computeParams = {
          'ids': ids,
          'locale': locale,
          'mimirResults': mimirResults,
          'token': token,
        };

        // Process database query in compute function
        compute<Map<String, dynamic>, List<Draft>>(
                _processDbResultsInBackground, computeParams)
            .then((pageResults) {
          // Save this page in cache
          _searchCache.put('${_currentQuery}_page_$pageKey', pageResults);

          // Update state after the frame
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              setState(() {
                isLoading = false;
                resultsLoading = false;
              });

              // Show complete results
              final isLastPage = pageResults.length < _pageSize;
              if (isLastPage) {
                _pagingController.appendLastPage(pageResults);
              } else {
                _pagingController.appendPage(pageResults, pageKey + 1);
              }
            }
          });
        }).catchError((error) {
          if (kDebugMode) {
            print('Error processing DB results: $error');
          }

          // In case of error, show basic results after the frame
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              setState(() {
                isLoading = false;
                resultsLoading = false;
              });
              _pagingController.error = error;
            }
          });
        });
      } else {
        // For subsequent pages, use stored results
        final allMimirResults = _searchCache.getAllMimirResults(_currentQuery);
        if (allMimirResults == null) {
          _pagingController.appendLastPage([]);
          return;
        }

        total = _searchCache.getTotalResults(_currentQuery) ?? 0;
        final start = offset.clamp(0, allMimirResults.length);
        final end = (offset + limit).clamp(0, allMimirResults.length);
        mimirResults = allMimirResults.sublist(start, end);

        // Similar processing for subsequent pages
        final List<String> ids =
            mimirResults.map((result) => result['id']!).toList();
        final String locale = context.locale.toString();

        // Prepare parameters for computation
        final computeParams = {
          'ids': ids,
          'locale': locale,
          'mimirResults': mimirResults,
          'token': token,
        };

        // Process database query in compute function
        compute<Map<String, dynamic>, List<Draft>>(
                _processDbResultsInBackground, computeParams)
            .then((pageResults) {
          // Save this page in cache
          _searchCache.put('${_currentQuery}_page_$pageKey', pageResults);

          // Show results
          final isLastPage = pageResults.length < _pageSize;
          if (isLastPage) {
            _pagingController.appendLastPage(pageResults);
          } else {
            _pagingController.appendPage(pageResults, pageKey + 1);
          }

          // Update state
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              setState(() {
                isLoading = false;
                resultsLoading = false;
              });
            }
          });
        }).catchError((error) {
          if (kDebugMode) {
            print('Error loading DB details on page $pageKey: $error');
          }
          _pagingController.error = error;
        });
      }
    } catch (e) {
      if (kDebugMode) {
        print('Error in pagination: $e');
      }

      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          setState(() {
            isLoading = false;
            mimirLoading = false;
            resultsLoading = false;
          });
          _pagingController.error = e;
        }
      });
    }
  }

  // Background processing function for database operations
  static Future<List<Draft>> _processDbResultsInBackground(
      Map<String, dynamic> params) async {
    final List<String> ids = params['ids'];
    final String locale = params['locale'];
    final List<Map<String, String>> mimirResults = params['mimirResults'];
    final RootIsolateToken token = params['token'];

    BackgroundIsolateBinaryMessenger.ensureInitialized(token);

    final database = AppDatabase();

    try {
      // Single query to get all details
      final String searchQuery = '''
      SELECT DISTINCT
        m.id AS id,
        t.title AS title,
        m.country AS country,
        m.city AS city,
        m.activity AS activity,
        m.draft AS draft,
        m.thumbnail AS thumbnail,
        t.languages_code AS locale,
        m.date AS date,
        t.languages_code AS languages_code,
        t.pdf AS pdf
      FROM
        messages AS m
      LEFT JOIN
        translations AS t
      ON
        m.id = t.message_id
      WHERE
        t.languages_code = ?
        AND m.id IN (${List.filled(ids.length, '?').join(',')})
      ORDER BY m.date DESC
      ''';

      // Prepare parameters: first the locale, then the IDs
      final variables = [
        drift.Variable.withString(locale),
        ...ids.map((id) => drift.Variable.withString(id)),
      ];

      final dbResults = await database
          .customSelect(
            searchQuery,
            variables: variables,
          )
          .get();

      // Create a map to relate IDs with DB results
      final Map<String, Map<String, dynamic>> resultsMap = {};

      for (final row in dbResults) {
        final id = row.read<String>('id');
        resultsMap[id] = {
          'id': id,
          'title': row.read<String>('title') ?? '',
          'date': row.read<DateTime>('date'),
          'activity': row.read<int>('activity') ?? 0,
          'thumbnail': row.read<String>('thumbnail') ?? '',
          'draft': row.read<int>('draft') ?? 0,
          'locale': row.read<String?>('locale') ?? '',
          'country': row.read<String>('country') ?? '',
          'city': row.read<String>('city') ?? '',
          'pdf': row.read<String?>('pdf') ?? '',
          'languagesCode': row.read<String>('languages_code') ?? '',
        };
      }

      // Combine Mimir information with DB results
      final List<Draft> pageResults = [];
      for (final result in mimirResults) {
        final id = result['id']!;
        if (resultsMap.containsKey(id)) {
          final dbData = resultsMap[id]!;
          // Create a new Draft object with DB and Mimir data
          final draft = Draft(
            id: id,
            title: dbData['title'] as String,
            date: dbData['date'] as DateTime,
            activity: dbData['activity'] as int,
            thumbnail: dbData['thumbnail'] as String,
            draft: dbData['draft'] as int,
            locale: dbData['locale'] as String,
            body: result['content']!,
            position: int.tryParse(result['position'] ?? '0') ?? 0,
            length: int.tryParse(result['length'] ?? '0') ?? 0,
            country: dbData['country'] as String,
            city: dbData['city'] as String,
            pdf: dbData['pdf'] as String,
            languagesCode: dbData['languagesCode'] as String,
          );
          pageResults.add(draft);
        }
      }

      return pageResults;
    } catch (e) {
      // If there's an error, create basic results with available information
      final List<Draft> basicResults = mimirResults.map((result) {
        final content = result['content'] ?? '';
        // Extract a title from content if possible
        String title = 'No title';
        if (content.length > 30) {
          title = content.substring(0, 30).replaceAll('\n', ' ') + '...';
        } else if (content.isNotEmpty) {
          title = content.replaceAll('\n', ' ');
        }

        return Draft(
          id: result['id']!,
          title: title,
          date: DateTime.now(),
          activity: 0,
          thumbnail: '',
          draft: 0,
          locale: locale,
          body: content,
          position: int.tryParse(result['position'] ?? '0') ?? 0,
          length: int.tryParse(result['length'] ?? '0') ?? 0,
          country: '',
          city: '',
          pdf: '',
          languagesCode: locale,
        );
      }).toList();

      return basicResults;
    } finally {
      // Close database connection when done
      database.close();
    }
  }

  Future<void> _initAppDirectory() async {
    appDirectory = await getApplicationDocumentsDirectory();
  }

  Future<File?> _getThumbnail(String directoryPath, String fileId) async {
    final dir = Directory(directoryPath);

    if (!await dir.exists()) {
      await dir.create(recursive: true);
    }

    final entities = await dir.list().toList();

    for (var entity in entities) {
      if (entity is File) {
        final fileName = entity.uri.pathSegments.last;
        if (fileName == '$fileId+SD.jpg') {
          return entity;
        }
      }
    }

    final String url =
        '$_baseUrl/assets/$fileId?access_token=$_token&width=320&height=180&quality=50&fit=cover&format=jpg';
    final String thumbnailPath = '$directoryPath/$fileId+SD.jpg';
    if (fileId == '') {
      return null;
    }
    try {
      await Dio().download(url, thumbnailPath);
      return File(thumbnailPath);
    } catch (e) {
      if (kDebugMode) {
        print('Error downloading thumbnail: $e');
      }
      return null;
    }
  }

  Widget _buildThumbnail(String thumbnailId) {
    // Evitar cargar miniaturas para thumbnailId vacío
    if (thumbnailId.isEmpty) {
      return Image.asset(
        'assets/image/default_thumbnail.jpg',
        height: 80,
        width: 100,
        fit: BoxFit.cover,
        alignment: Alignment.center,
        cacheHeight: 160,
        cacheWidth: 200,
        filterQuality: FilterQuality.low,
        gaplessPlayback: true,
      );
    }

    return FutureBuilder<File?>(
      future: _getThumbnail(
          '${appDirectory.path}/LGCC_Search/${context.locale.toString()}/thumbnails/',
          thumbnailId),
      builder: (context, snapshot) {
        return Skeletonizer(
          key: ValueKey(
              'skeleton_thumbnail_${thumbnailId}_${snapshot.connectionState}'),
          enableSwitchAnimation: true,
          enabled: snapshot.connectionState != ConnectionState.done,
          effect: const ShimmerEffect(
            baseColor: Color(0xFFf1f5eb),
            highlightColor: Colors.white30,
            duration: Duration(milliseconds: 1000),
          ),
          child: snapshot.hasData && snapshot.data != null
              ? Image.file(
                  snapshot.data!,
                  height: 80,
                  width: 100,
                  fit: BoxFit.cover,
                  cacheHeight: 180,
                  cacheWidth: 320,
                  filterQuality: FilterQuality.low,
                  gaplessPlayback: true,
                  errorBuilder: (context, error, stackTrace) {
                    return Image.asset(
                      'assets/image/default_thumbnail.jpg',
                      height: 80,
                      width: 100,
                      fit: BoxFit.cover,
                    );
                  },
                )
              : Image.asset(
                  'assets/image/default_thumbnail.jpg',
                  height: 80,
                  width: 100,
                  fit: BoxFit.cover,
                  alignment: Alignment.center,
                  cacheHeight: 160,
                  cacheWidth: 200,
                  filterQuality: FilterQuality.low,
                  gaplessPlayback: true,
                ),
        );
      },
    );
  }

  // Modified to handle highlighting efficiently
  Future<List<TextSpan>> _getHighlightedSpansAsync(
      String text, String searchText) {
    if (searchText.isEmpty) {
      return Future.value([TextSpan(text: text)]);
    }

    // The text is already clean of HTML, we can use it directly
    final String plainText = text;

    // If the text is very short, don't process it
    if (plainText.length < 3) {
      return Future.value([TextSpan(text: plainText)]);
    }

    // Cache for results to avoid recalculations
    final String cacheKey = '$plainText:$searchText';
    if (_highlightedSpansCache.containsKey(cacheKey)) {
      return Future.value(_highlightedSpansCache[cacheKey]!);
    }

    // Run the highlighting in a compute function to avoid blocking the UI
    return compute(_computeHighlightedSpans, [plainText, searchText])
        .then((spans) {
      _highlightedSpansCache[cacheKey] = spans;
      return spans;
    }).catchError((error) {
      // Fallback in case of error
      return [TextSpan(text: plainText)];
    });
  }

  // Static method for compute
  static List<TextSpan> _computeHighlightedSpans(List<dynamic> params) {
    final String plainText = params[0];
    final String searchText = params[1];

    final List<TextSpan> spans = [];

    // Extract keywords from the search and filter very short ones
    final List<String> keywords = searchText
        .trim()
        .toLowerCase()
        .split(RegExp(r'\s+'))
        .where((word) => word.isNotEmpty && word.length >= 3)
        .toList();

    if (keywords.isEmpty) {
      return [TextSpan(text: plainText)];
    }

    // Text in lowercase for comparisons
    final String lowerText = plainText.toLowerCase();

    // Create a list of all matches
    final List<_Match> allMatches = [];

    // Use a more efficient approach to find matches
    for (final keyword in keywords) {
      // For words longer than 4 letters, consider the root as the first n-1 letters
      String baseWord = keyword;
      if (baseWord.length > 4) {
        baseWord = baseWord.substring(0, baseWord.length - 1);
      }

      // Search for the base word in the text
      int startIndex = 0;
      while (true) {
        final int index = lowerText.indexOf(baseWord, startIndex);
        if (index == -1) break;

        // Find the end of the word
        int endIndex = index + baseWord.length;
        while (endIndex < lowerText.length &&
            _isWordCharacter(lowerText[endIndex])) {
          endIndex++;
        }

        // Add the match
        allMatches.add(_Match(
          start: index,
          end: endIndex,
          text: plainText.substring(index, endIndex),
        ));

        // Continue from the end of this match
        startIndex = endIndex;
      }
    }

    // If there are no matches, return the original text
    if (allMatches.isEmpty) {
      return [TextSpan(text: plainText)];
    }

    // Sort matches by position
    allMatches.sort((a, b) => a.start.compareTo(b.start));

    // Handle overlapping matches
    final List<_Match> mergedMatches = [];
    if (allMatches.isNotEmpty) {
      _Match current = allMatches.first;
      for (int i = 1; i < allMatches.length; i++) {
        final _Match next = allMatches[i];
        if (current.end >= next.start) {
          // Matches overlap, merge them
          current = _Match(
            start: current.start,
            end: math.max(current.end, next.end),
            text: plainText.substring(
                current.start, math.max(current.end, next.end)),
          );
        } else {
          // No overlap, add the current one to the result and move to the next
          mergedMatches.add(current);
          current = next;
        }
      }
      mergedMatches.add(current); // Add the last match
    }

    // Build spans from merged matches
    int lastIndex = 0;
    for (final match in mergedMatches) {
      if (match.start > lastIndex) {
        spans.add(TextSpan(
          text: plainText.substring(lastIndex, match.start),
        ));
      }

      spans.add(TextSpan(
        text: plainText.substring(match.start, match.end),
        style: const TextStyle(backgroundColor: Color(0xFFfff930)),
      ));

      lastIndex = match.end;
    }

    // Add remaining text
    if (lastIndex < plainText.length) {
      spans.add(TextSpan(
        text: plainText.substring(lastIndex),
      ));
    }

    return spans;
  }

  // Check if a character is part of a word
  static bool _isWordCharacter(String char) {
    return RegExp(r'[a-zñáéíóúüA-ZÑÁÉÍÓÚÜ0-9]').hasMatch(char);
  }

  // Caché para los spans resaltados
  final Map<String, List<TextSpan>> _highlightedSpansCache = {};

  String _getCountryName(String countryCode) {
    if (countryCode.isEmpty) {
      return 'N/A';
    }

    try {
      return CountryCodes.detailsFromAlpha2(countryCode).name.toString();
    } catch (e) {
      // Si no se encuentra el código de país, devolver el código como está
      return countryCode;
    }
  }

  // Widget optimizado para texto de ubicación
  Widget _buildLocationText(Draft message) {
    // Evitar cálculos costosos en cada reconstrucción
    final String locationText = message.city.isNotEmpty
        ? '${message.city}, ${_getCountryNameCached(message.country)}'
        : _getCountryNameCached(message.country);

    return Text(
      locationText,
      style: const TextStyle(fontSize: 14, height: 1.2),
      overflow: TextOverflow.ellipsis,
      maxLines: 1,
    );
  }

  // Versión en caché de _getCountryName
  String _getCountryNameCached(String countryCode) {
    if (countryCode.isEmpty) {
      return 'N/A';
    }

    if (_countryNameCache.containsKey(countryCode)) {
      return _countryNameCache[countryCode]!;
    }

    try {
      final name = CountryCodes.detailsFromAlpha2(countryCode).name.toString();
      _countryNameCache[countryCode] = name;
      return name;
    } catch (e) {
      _countryNameCache[countryCode] = countryCode;
      return countryCode;
    }
  }

  @override
  Widget build(BuildContext context) {
    return BaseScreen(
      title: 'search'.tr(),
      showSearchBar: true,
      showSettingsButton: true,
      searchController: _searchController,
      onSearchChanged: (_) {},
      onSearchSubmitted: _onSearch,
      searchHintText: 'search_placeholder'.tr(),
      child: Stack(
        children: [
          // Logo cuando no hay búsqueda activa
          if (!_isSearching || _currentQuery.isEmpty)
            Positioned(
              bottom: 80.0,
              left: 0,
              right: 0,
              child: Center(
                child: Image.asset(
                  'assets/image/logo.png',
                  width: 200,
                ),
              ),
            ),

          // Resultados de búsqueda
          if (_isSearching)
            Padding(
              padding: const EdgeInsets.all(20.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  _buildSearchHeader(),
                  const SizedBox(height: 16),
                  Expanded(
                    child: PagedListView<int, Draft>(
                      pagingController: _pagingController,
                      scrollController: _scrollController,
                      addAutomaticKeepAlives: false,
                      addRepaintBoundaries: true,
                      physics: const AlwaysScrollableScrollPhysics(),
                      builderDelegate: PagedChildBuilderDelegate<Draft>(
                        itemBuilder: (context, message, index) =>
                            _buildDefaultItem(message),
                        firstPageProgressIndicatorBuilder: (_) => Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              AnimatedBuilder(
                                animation: _pulseAnimation,
                                builder: (context, child) {
                                  return Transform.scale(
                                    scale: _pulseAnimation.value,
                                    child: child,
                                  );
                                },
                                child: const CircularProgressIndicator(
                                  color: Color(0XFF6b8e23),
                                  strokeWidth: 3,
                                ),
                              ),
                              const SizedBox(height: 16),
                              Container(
                                padding: const EdgeInsets.symmetric(
                                    horizontal: 20, vertical: 10),
                                decoration: BoxDecoration(
                                  color:
                                      const Color(0xFF6b8e23).withOpacity(0.15),
                                  borderRadius: BorderRadius.circular(12),
                                  border: Border.all(
                                    color: const Color(0xFF6b8e23)
                                        .withOpacity(0.3),
                                    width: 1,
                                  ),
                                ),
                                child: Text(
                                  mimirLoading
                                      ? 'searching_in_progress'.tr()
                                      : 'loading_details'.tr(),
                                  style: const TextStyle(
                                    fontSize: 14,
                                    fontWeight: FontWeight.w500,
                                    color: Color(0xFF6b8e23),
                                  ),
                                ),
                              ),
                              // Mostrar el contador de resultados tan pronto como tengamos los IDs de Mimir
                              // sin esperar a que se carguen los detalles
                              if (!mimirLoading && totalResults > 0)
                                Padding(
                                  padding: const EdgeInsets.only(top: 8),
                                  child: Container(
                                    padding: const EdgeInsets.symmetric(
                                        horizontal: 16, vertical: 8),
                                    decoration: BoxDecoration(
                                      color: const Color(0xFF6b8e23)
                                          .withOpacity(0.15),
                                      borderRadius: BorderRadius.circular(12),
                                      border: Border.all(
                                        color: const Color(0xFF6b8e23)
                                            .withOpacity(0.3),
                                        width: 1,
                                      ),
                                    ),
                                    child: Text(
                                      "$totalResults ${'results'.plural(totalResults)} ${'found'.tr()}",
                                      style: const TextStyle(
                                        fontSize: 16,
                                        fontWeight: FontWeight.bold,
                                        color: Color(0xFF6b8e23),
                                      ),
                                    ),
                                  ),
                                ),
                            ],
                          ),
                        ),
                        newPageProgressIndicatorBuilder: (_) => Container(
                          padding: const EdgeInsets.symmetric(vertical: 16),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Container(
                                padding: const EdgeInsets.symmetric(
                                    horizontal: 16, vertical: 10),
                                decoration: BoxDecoration(
                                  color:
                                      const Color(0xFF6b8e23).withOpacity(0.15),
                                  borderRadius: BorderRadius.circular(12),
                                  border: Border.all(
                                    color: const Color(0xFF6b8e23)
                                        .withOpacity(0.3),
                                    width: 1,
                                  ),
                                ),
                                child: Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    AnimatedBuilder(
                                      animation: _pulseAnimation,
                                      builder: (context, child) {
                                        return Transform.scale(
                                          scale: _pulseAnimation.value,
                                          child: child,
                                        );
                                      },
                                      child: const SizedBox(
                                        width: 14,
                                        height: 14,
                                        child: CircularProgressIndicator(
                                          strokeWidth: 2,
                                          valueColor:
                                              AlwaysStoppedAnimation<Color>(
                                                  Color(0xFF6b8e23)),
                                        ),
                                      ),
                                    ),
                                    const SizedBox(width: 10),
                                    Text(
                                      'loading_more'.tr(),
                                      style: const TextStyle(
                                        fontSize: 13,
                                        fontWeight: FontWeight.w500,
                                        color: Color(0xFF6b8e23),
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            ],
                          ),
                        ),
                        noItemsFoundIndicatorBuilder: (_) => Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Container(
                                padding: const EdgeInsets.all(24),
                                decoration: BoxDecoration(
                                  color:
                                      const Color(0xFF6b8e23).withOpacity(0.1),
                                  shape: BoxShape.circle,
                                ),
                                child: const Icon(
                                  Icons.search_off_rounded,
                                  size: 48,
                                  color: Color(0XFF6b8e23),
                                ),
                              ),
                              const SizedBox(height: 16),
                              Container(
                                padding: const EdgeInsets.symmetric(
                                    horizontal: 20, vertical: 12),
                                decoration: BoxDecoration(
                                  color:
                                      const Color(0xFF6b8e23).withOpacity(0.15),
                                  borderRadius: BorderRadius.circular(12),
                                  border: Border.all(
                                    color: const Color(0xFF6b8e23)
                                        .withOpacity(0.3),
                                    width: 1,
                                  ),
                                ),
                                child: Text(
                                  'no_results'.tr(),
                                  style: const TextStyle(
                                    fontSize: 16,
                                    fontWeight: FontWeight.w600,
                                    color: Color(0xFF6b8e23),
                                  ),
                                ),
                              ),
                              const SizedBox(height: 8),
                              Padding(
                                padding:
                                    const EdgeInsets.symmetric(horizontal: 40),
                                child: Text(
                                  'try_different_search'.tr(),
                                  textAlign: TextAlign.center,
                                  style: TextStyle(
                                    fontSize: 14,
                                    color: Colors.grey[700],
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }

  // Widget para el encabezado de búsqueda
  Widget _buildSearchHeader() {
    Widget loadingIndicator(String text) {
      return Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        decoration: BoxDecoration(
          color: const Color(0xFF6b8e23).withOpacity(0.15),
          borderRadius: BorderRadius.circular(12),
          border: Border.all(
            color: const Color(0xFF6b8e23).withOpacity(0.3),
            width: 1,
          ),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            AnimatedBuilder(
              animation: _pulseAnimation,
              builder: (context, child) {
                return Transform.scale(
                  scale: _pulseAnimation.value,
                  child: child,
                );
              },
              child: const SizedBox(
                width: 14,
                height: 14,
                child: CircularProgressIndicator(
                  strokeWidth: 2,
                  valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF6b8e23)),
                ),
              ),
            ),
            const SizedBox(width: 8),
            Text(
              text,
              style: const TextStyle(
                fontSize: 13,
                fontWeight: FontWeight.w500,
                color: Color(0xFF6b8e23),
              ),
            ),
          ],
        ),
      );
    }

    return Row(
      children: [
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Expanded(
                    child: Text(
                      '${'searching'.tr()}: "$_currentQuery"',
                      style: const TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                  // Mostrar el contador de resultados tan pronto como tengamos los IDs de Mimir
                  // sin esperar a que se carguen los detalles
                  if (!mimirLoading && totalResults > 0)
                    Container(
                      padding: const EdgeInsets.symmetric(
                          horizontal: 8, vertical: 4),
                      decoration: BoxDecoration(
                        color: const Color(0xFF6b8e23).withOpacity(0.15),
                        borderRadius: BorderRadius.circular(8),
                      ),
                      child: Text(
                        "$totalResults ${'results'.plural(totalResults)}",
                        style: const TextStyle(
                          fontSize: 14,
                          fontWeight: FontWeight.bold,
                          color: Color(0xFF6b8e23),
                        ),
                      ),
                    ),
                ],
              ),
            ],
          ),
        ),
        if (mimirLoading)
          loadingIndicator('searching_in_progress'.tr())
        else if (resultsLoading && totalResults > 0)
          loadingIndicator('loading_details'.tr()),
      ],
    );
  }

  Widget _buildDefaultItem(Draft message) {
    // Usar un widget de construcción diferida para mejorar el rendimiento
    return RepaintBoundary(
      child: Card(
        key: ValueKey(
            'search_result_${message.id}_${message.date.millisecondsSinceEpoch}'),
        color: const Color(0xFFdfe6ce),
        margin: const EdgeInsets.only(bottom: 16),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
        child: InkWell(
          onTap: () => nav.pushScreenWithoutNavBar(
              context,
              TextViewer(
                  data: message,
                  searchTerm: message.body != null
                      ? _getMatchedText(message.body!, _currentQuery)
                      : _currentQuery)),
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    ClipRRect(
                      borderRadius: BorderRadius.circular(8),
                      child: Container(
                        width: 100,
                        height: 80,
                        decoration: const BoxDecoration(
                          color: Color(0xFFf1f5eb),
                        ),
                        child: _buildThumbnail(message.thumbnail),
                      ),
                    ),
                    const SizedBox(width: 16),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          _buildItemTitle(message),
                          const SizedBox(height: 5),
                          Text(
                            utils.formatDate(
                                message.date, context.locale.toString()),
                            style: const TextStyle(fontSize: 14, height: 1),
                          ),
                          const SizedBox(height: 5),
                          _buildLocationText(message),
                          const SizedBox(height: 5),
                          Text(
                            "${plural('activity', 1)} ${message.activity}",
                            style: const TextStyle(fontSize: 14, height: 1),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
                if (message.body != null && message.body!.isNotEmpty)
                  _buildSnippet(message.body!, _currentQuery),
              ],
            ),
          ),
        ),
      ),
    );
  }

  // Widget optimizado para el título del item
  Widget _buildItemTitle(Draft message) {
    if (message.title.isEmpty) {
      return Text(
        utils.formatDate(message.date, context.locale.toString()),
        style: const TextStyle(
          fontWeight: FontWeight.bold,
          fontSize: 16,
          height: 1.2,
        ),
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
      );
    }

    return _buildHighlightedText(
      message.title,
      _currentQuery,
      const TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: 16,
        height: 1.2,
        color: Colors.black,
        fontFamily: 'Outfit',
      ),
      maxLines: 2,
    );
  }

  // Widget optimizado para el snippet
  Widget _buildSnippet(String snippet, String query) {
    return Padding(
      padding: const EdgeInsets.only(top: 16),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
        decoration: BoxDecoration(
          color: Colors.white.withOpacity(0.5),
          borderRadius: BorderRadius.circular(8),
        ),
        child: _buildHighlightedText(
          snippet,
          query,
          const TextStyle(
            fontSize: 14,
            color: Colors.black87,
            height: 1.5,
            fontFamily: 'Outfit',
          ),
          maxLines: 3,
        ),
      ),
    );
  }

  // Widget optimizado para texto resaltado
  Widget _buildHighlightedText(String text, String searchText, TextStyle style,
      {int maxLines = 2}) {
    // Use simple text display initially to prevent blocking
    Widget simpleText = RichText(
      maxLines: maxLines,
      overflow: TextOverflow.ellipsis,
      text: TextSpan(
        text: text,
        style: style,
      ),
      textScaler: TextScaler.linear(1.0),
    );

    // If text is simple or search is empty, just show as is
    if (text.length < 50 || searchText.isEmpty) {
      return simpleText;
    }

    // For complex highlighting, use an optimized approach with caching and fallback
    final String cacheKey = '$text:$searchText';
    if (_highlightedSpansCache.containsKey(cacheKey)) {
      // Use cached spans if available
      return RichText(
        maxLines: maxLines,
        overflow: TextOverflow.ellipsis,
        text: TextSpan(
          children: _highlightedSpansCache[cacheKey],
          style: style,
        ),
        textScaler: TextScaler.linear(1.0),
      );
    }

    // Trigger async highlighting and use simple text initially
    _getHighlightedSpansAsync(text, searchText).then((spans) {
      // Force a rebuild after spans are computed
      if (mounted) setState(() {});
    });

    return simpleText;
  }

  String _getMatchedText(String content, String searchQuery) {
    if (searchQuery.isEmpty) return '';

    // Convertir a minúsculas para búsqueda insensible a mayúsculas/minúsculas
    final lowerContent = content.toLowerCase();
    final lowerQuery = searchQuery.toLowerCase();

    // Buscar la coincidencia exacta
    final int index = lowerContent.indexOf(lowerQuery);
    if (index != -1) {
      // Devolver el texto original que coincidió, no la versión en minúsculas
      return content.substring(index, index + searchQuery.length);
    }

    // Si no hay coincidencia exacta, buscar palabras individuales
    final queryWords = lowerQuery
        .split(RegExp(r'\s+'))
        .where((word) => word.length > 2)
        .toList();

    for (final word in queryWords) {
      final wordIndex = lowerContent.indexOf(word);
      if (wordIndex != -1) {
        // Devolver la primera palabra que coincida
        return content.substring(wordIndex, wordIndex + word.length);
      }
    }

    return searchQuery; // Fallback al término de búsqueda original
  }
}
