import 'dart:io';
import 'dart:async';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

import 'package:country_codes/country_codes.dart';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_inapp_notifications/flutter_inapp_notifications.dart';
import 'package:path_provider/path_provider.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
import 'package:search_engine/database.dart';
import 'package:search_engine/screens/pdf.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:photo_view/photo_view.dart';
import 'package:gal/gal.dart';
import 'package:search_engine/utils.dart' as utils;
import 'package:search_engine/services/config_service.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:search_engine/services/notification_service.dart';

// ignore: must_be_immutable
class TextViewer extends StatefulWidget {
  final Draft data;
  String? searchTerm;

  TextViewer({super.key, required this.data, this.searchTerm});

  @override
  State<TextViewer> createState() => _TextViewerState();
}

class _TextViewerState extends State<TextViewer>
    with SingleTickerProviderStateMixin {
  bool hdThumbnail = false;
  bool isLoading = true;
  bool isLoadingBody = true;
  String thumbnail = "";
  String messageBody = "";
  final _baseUrl = dotenv.env['BASE_URL'];
  final _token = dotenv.env['TOKEN'];
  String locale = 'es';
  bool isFav = false;
  Timer? _debounce;
  late TextEditingController _searchController;
  bool searching = false;
  bool downloading = false;
  int downloadProgress = 0;
  ScrollController _scrollController = ScrollController();
  int currentResultIndex = -1;
  bool _hasShownNotification = false;
  String highlightedHtml = '';
  List<GlobalKey> resultKeys = [];
  late Future<AppDatabase> _databaseFuture;
  bool _isSearchExpanded = false;
  bool _isMenuOpen = false;
  bool _isHtmlReady = false;
  final bool _isNavigating = false;
  Timer? _renderDebounce;
  String _cachedHtml = '';
  bool _isFirstRender = true;
  final _fabKey = GlobalKey<ExpandableFabState>();

  // Lista para almacenar las posiciones de los resultados de búsqueda
  List<SearchResultInfo> _searchResultPositions = [];

  // Clave global para el contenedor del HTML
  final GlobalKey _htmlContainerKey = GlobalKey();

  // Variables para el control de búsqueda
  bool _isSearching = false;
  String? _highlightedResultKey;
  final ValueNotifier<String> _searchButtonController =
      ValueNotifier<String>('');

  // Nuevas variables para el enfoque basado en párrafos
  List<ParagraphData> _paragraphs = [];
  Map<String, GlobalKey> _paragraphKeys = {};
  List<SearchMatch> _searchMatches = [];

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _databaseFuture = Future.value(AppDatabase());
    _initializeData();
  }

  Future<void> _initializeData() async {
    await Future.wait<void>([
      getLocale(),
      checkFavorite(),
      _getConfig(),
    ]);
    _searchController = TextEditingController(text: widget.searchTerm);
    _loadMessageBody();
  }

  Future<void> _loadMessageBody() async {
    if (_isNavigating) return;

    setState(() {
      isLoadingBody = true;
      _isFirstRender = true;
    });

    try {
      // Primero cargar la imagen con timeout
      await _checkAndDownloadThumbnail();

      // Solo después de que la imagen esté lista, cargar el contenido
      final token = RootIsolateToken.instance;
      if (token == null) {
        throw Exception('RootIsolateToken is not initialized');
      }

      final body = await compute(
        _loadMessageBodyIsolate,
        [widget.data.id.toString(), widget.data.languagesCode, token],
      );

      if (mounted) {
        setState(() {
          messageBody = body ?? widget.data.body ?? '';
          _cachedHtml = messageBody;
          isLoadingBody = false;
        });

        // Procesar el HTML en párrafos
        _processParagraphs(messageBody);

        _renderDebounce?.cancel();
        _renderDebounce = Timer(const Duration(milliseconds: 100), () {
          if (mounted) {
            setState(() {
              _isHtmlReady = true;
            });
          }
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          messageBody = widget.data.body ?? '';
          _cachedHtml = messageBody;
          isLoadingBody = false;
        });

        // Procesar el HTML en párrafos incluso en caso de error
        _processParagraphs(messageBody);
      }
    }
  }

  static Future<String?> _loadMessageBodyIsolate(List<dynamic> params) async {
    final String messageId = params[0];
    final String languageCode = params[1];
    final RootIsolateToken token = params[2];

    BackgroundIsolateBinaryMessenger.ensureInitialized(token);
    final database = AppDatabase();

    try {
      final body = await database.getMessageBody(messageId, languageCode);
      await database.close();
      return body;
    } catch (e) {
      await database.close();
      return null;
    }
  }

  @override
  void dispose() {
    _searchController.dispose();
    _scrollController.dispose();
    _debounce?.cancel();
    _renderDebounce?.cancel();
    _databaseFuture.then((db) => db.close());
    super.dispose();
  }

  Future<File?> _getThumbnail(String fileId) async {
    final directory = await getApplicationDocumentsDirectory();
    final directoryPath = '${directory.path}/LGCC_Search/$locale/thumbnails';
    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' || fileName == '$fileId+HD') {
          return entity;
        }
      }
    }
    return null;
  }

  Future<void> checkFavorite() async {
    final database = await _databaseFuture;
    final res = await database.checkFavorite(widget.data.id);
    if (mounted) {
      setState(() {
        isFav = res;
      });
    }
  }

  void _toggleFavorite() async {
    final database = await _databaseFuture;
    await database.toggleFavorite(widget.data.id);
    setState(() {
      isFav = !isFav;
    });
  }

  Future<File?> _downloadThumbnail(String url, String id,
      {bool isHighDefinition = false}) async {
    var appDir = await getApplicationDocumentsDirectory();
    final thumbnailDir =
        Directory('${appDir.path}/LGCC_Search/$locale/thumbnails/');
    final fileName = '$id${isHighDefinition ? '+HD' : '+SD'}.jpg';
    final existingFile = File('${thumbnailDir.path}/$fileName');
    if (existingFile.existsSync()) {
      return existingFile;
    }
    if (id == '') {
      return null;
    }

    try {
      final token = RootIsolateToken.instance;
      if (token == null) {
        throw Exception('RootIsolateToken is not initialized');
      }

      final result = await compute(
        _downloadThumbnailIsolate,
        {
          'url': url,
          'path': '${thumbnailDir.path}/$fileName',
          'token': token,
        },
      );

      if (result != null) {
        final resultFile = File(result);
        if (resultFile.existsSync()) {
          return resultFile;
        }
      }
      return null;
    } catch (e) {
      if (kDebugMode) {
        print('Error downloading thumbnail: $e');
      }
      return null;
    }
  }

  static Future<String?> _downloadThumbnailIsolate(
      Map<String, dynamic> params) async {
    final String url = params['url'];
    final String path = params['path'];
    final RootIsolateToken token = params['token'];

    BackgroundIsolateBinaryMessenger.ensureInitialized(token);

    try {
      final dio = Dio();
      final completer = Completer<String?>();

      // Timeout para iniciar la descarga
      Timer(const Duration(seconds: 3), () {
        if (!completer.isCompleted) {
          completer.complete(null);
        }
      });

      await dio.download(
        url,
        path,
        options: Options(
          responseType: ResponseType.bytes,
          followRedirects: true,
          receiveTimeout: const Duration(seconds: 3),
        ),
        onReceiveProgress: (actualBytes, totalBytes) {
          if (actualBytes == 0 && !completer.isCompleted) {
            completer.complete(null);
          }
        },
      );

      if (!completer.isCompleted) {
        completer.complete(path);
      }

      return await completer.future;
    } catch (e) {
      if (kDebugMode) {
        print('Error in isolate downloading thumbnail: $e');
      }
      return null;
    }
  }

  Future<void> _checkAndDownloadThumbnail() async {
    setState(() {
      isLoading = true;
    });

    try {
      final completer = Completer<void>();

      // Timer para el timeout de la imagen
      Timer(const Duration(seconds: 5), () {
        if (!completer.isCompleted) {
          if (kDebugMode) {
            print('Timeout loading thumbnail');
          }
          completer.complete();
        }
      });

      // Intentar cargar la imagen local primero
      final File? localThumbnail = await _getThumbnail(widget.data.thumbnail);
      if (localThumbnail != null && localThumbnail.existsSync()) {
        if (mounted) {
          setState(() {
            thumbnail = localThumbnail.path;
            isLoading = false;
          });
        }
        if (!completer.isCompleted) completer.complete();
        return;
      }

      // Si no hay imagen local, intentar descargar
      if (widget.data.thumbnail.isNotEmpty) {
        final String url =
            '$_baseUrl/assets/${widget.data.thumbnail}?access_token=$_token&format=jpg';
        final File? downloadedThumbnail = await _downloadThumbnail(
          url,
          widget.data.thumbnail,
          isHighDefinition: hdThumbnail,
        );

        if (mounted) {
          setState(() {
            thumbnail = downloadedThumbnail?.path ?? '';
            isLoading = false;
          });
        }
      } else {
        if (mounted) {
          setState(() {
            thumbnail = '';
            isLoading = false;
          });
        }
      }

      if (!completer.isCompleted) completer.complete();
      return await completer.future;
    } catch (e) {
      if (kDebugMode) {
        print('Error loading thumbnail: $e');
      }
      if (mounted) {
        setState(() {
          thumbnail = '';
          isLoading = false;
        });
      }
    }
  }

  Future<void> getLocale() async {
    final pltLocale = Platform.localeName.split('_')[0];
    final savedLocale = await ConfigService.getLocale();
    if (mounted) {
      setState(() {
        locale = savedLocale;
      });
    }
  }

  Future<void> _getConfig() async {
    hdThumbnail = await ConfigService.getHdThumbnails();
    await _checkAndDownloadThumbnail();
  }

  void _onHtmlRendered() {
    if (kDebugMode) {
      print('🎨 HTML renderizado completamente');
    }

    if (!_isFirstRender) return;

    setState(() {
      _isHtmlReady = true;
      _isFirstRender = false;
    });

    // Si hay un término de búsqueda inicial y posición, desplazarse a esa posición
    if (widget.data.position > 0 && widget.data.length > 0) {
      if (kDebugMode) {
        print(
            '🔄 Desplazándose a la posición encontrada: ${widget.data.position}');
      }

      // Dar tiempo para que el DOM se actualice completamente
      Future.delayed(const Duration(milliseconds: 200), () {
        if (mounted) {
          _scrollToPosition(widget.data.position, widget.data.length);
        }
      });
    }
    // Si solo hay un término de búsqueda pero no posición, usar el método anterior
    else if (widget.searchTerm != null && widget.searchTerm!.isNotEmpty) {
      if (kDebugMode) {
        print('🔄 Ejecutando búsqueda inicial: ${widget.searchTerm}');
      }
      _searchController.text = widget.searchTerm!;
      _isSearchExpanded = true;
      _onSearch();
    }

    // Si ya hay resultados de búsqueda, calcular sus posiciones
    if (resultKeys.isNotEmpty) {
      // Dar tiempo para que el DOM se actualice completamente
      Future.delayed(const Duration(milliseconds: 100), () {
        if (mounted) {
          _findSearchResultPositions();
        }
      });
    }
  }

  // Método para desplazarse a una posición específica en el texto
  void _scrollToPosition(int position, int length) {
    if (position <= 0 || !_isHtmlReady) return;

    // Obtener el contexto del contenedor HTML
    final BuildContext? htmlContext = _htmlContainerKey.currentContext;
    if (htmlContext == null) return;

    // Calcular la posición aproximada en el documento
    final RenderBox? htmlBox = htmlContext.findRenderObject() as RenderBox?;
    if (htmlBox == null) return;

    // Estimar la posición de desplazamiento basada en la posición del texto
    // Esto es una aproximación ya que no podemos mapear directamente caracteres a píxeles
    final double containerHeight = htmlBox.size.height;
    final String text = messageBody;

    // Calcular la proporción de la posición en el texto
    final double proportion = position / text.length.clamp(1, double.infinity);

    // Estimar la posición de desplazamiento
    final double scrollOffset = containerHeight * proportion;

    // Desplazarse a la posición estimada
    _scrollController.animateTo(
      scrollOffset.clamp(0, _scrollController.position.maxScrollExtent),
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );

    // Resaltar visualmente la sección encontrada
    setState(() {
      // Crear un marcador visual temporal
      _highlightedResultKey = 'search-result-found';
    });

    // Programar la eliminación del resaltado después de un tiempo
    Future.delayed(const Duration(seconds: 2), () {
      if (mounted) {
        setState(() {
          _highlightedResultKey = null;
        });
      }
    });
  }

  void _onSearch() {
    if (!_isHtmlReady) {
      if (kDebugMode) {
        print('⚠️ HTML no está listo para búsqueda');
      }
      return;
    }

    if (kDebugMode) {
      print('🔍 Iniciando búsqueda...');
      print('📝 Término de búsqueda: ${_searchController.text}');
    }

    // Limpiar resultados anteriores
    setState(() {
      _searchMatches = [];
      currentResultIndex = -1;
      _hasShownNotification = false;
    });

    // Actualizar el término de búsqueda
    setState(() {
      widget.searchTerm = _searchController.text;
      FocusManager.instance.primaryFocus?.unfocus();

      if (widget.searchTerm!.isEmpty) {
        return; // Exit early if search term is empty
      }
    });

    if (_paragraphs.isNotEmpty) {
      if (kDebugMode) {
        print('📄 Párrafos disponibles, procediendo a buscar...');
      }
      _searchInParagraphs(widget.searchTerm!);
    } else {
      if (kDebugMode) {
        print('⚠️ No hay párrafos disponibles');
      }
    }
  }

  void _showNotification() {
    NotificationService().showSearchNotification(
      title: 'search'.tr(),
      body: 'empty_results'.tr(),
    );
  }

  // Método para procesar el HTML en párrafos
  void _processParagraphs(String html) {
    _paragraphs = [];
    _paragraphKeys = {};

    if (kDebugMode) {
      print('🔄 Procesando HTML en párrafos...');
    }

    // Limpiar estilos en línea antes de procesar los párrafos
    final cleanedHtml = _removeFontFamily(html);

    // Dividir el HTML en párrafos
    final RegExp paragraphRegex = RegExp(r'<p[^>]*>(.*?)<\/p>', dotAll: true);
    final matches = paragraphRegex.allMatches(cleanedHtml);

    if (matches.isEmpty) {
      if (kDebugMode) {
        print(
            '⚠️ No se encontraron etiquetas <p> en el HTML, tratando todo como un solo párrafo');
      }

      // Si no hay párrafos, tratar todo el contenido como un solo párrafo
      final String id = 'paragraph-0';
      _paragraphs.add(ParagraphData(
        id: id,
        content: cleanedHtml,
        plainText: _stripHtml(cleanedHtml),
      ));
      _paragraphKeys[id] = GlobalKey();
    } else {
      int index = 0;
      for (final match in matches) {
        final String paragraphHtml = match.group(0) ?? '';
        if (paragraphHtml.isNotEmpty) {
          final String id = 'paragraph-$index';
          final String plainText = _stripHtml(paragraphHtml);

          if (plainText.trim().isNotEmpty) {
            _paragraphs.add(ParagraphData(
              id: id,
              content: paragraphHtml,
              plainText: plainText,
            ));
            _paragraphKeys[id] = GlobalKey();
            index++;
          }
        }
      }
    }

    if (kDebugMode) {
      print('📑 Procesados ${_paragraphs.length} párrafos');
    }

    // Si hay un término de búsqueda inicial, realizar la búsqueda
    if (widget.searchTerm != null && widget.searchTerm!.isNotEmpty) {
      _searchInParagraphs(widget.searchTerm!);
    }
  }

  // Método para eliminar etiquetas HTML y obtener texto plano
  String _stripHtml(String html) {
    // Primero reemplazar <br> con espacios para mantener la separación
    String text =
        html.replaceAll(RegExp(r'<br\s*\/?>', caseSensitive: false), ' ');

    // Luego eliminar todas las demás etiquetas HTML
    text = text.replaceAll(RegExp(r'<[^>]*>'), '');

    // Decodificar entidades HTML comunes
    text = text
        .replaceAll('&nbsp;', ' ')
        .replaceAll('&amp;', '&')
        .replaceAll('&lt;', '<')
        .replaceAll('&gt;', '>')
        .replaceAll('&quot;', '"')
        .replaceAll('&#39;', "'");

    // Normalizar espacios
    text = text.replaceAll(RegExp(r'\s+'), ' ').trim();

    return text;
  }

  // Método para buscar en los párrafos
  void _searchInParagraphs(String searchTerm) {
    if (searchTerm.isEmpty) {
      _searchMatches = [];
      return;
    }

    _searchMatches = [];
    final String lowerSearchTerm = searchTerm.toLowerCase();

    if (kDebugMode) {
      print('🔍 Buscando exactamente: "$searchTerm"');
    }

    for (int i = 0; i < _paragraphs.length; i++) {
      final paragraph = _paragraphs[i];
      final String lowerPlainText = paragraph.plainText.toLowerCase();

      int startIndex = 0;
      while (true) {
        final int matchIndex =
            lowerPlainText.indexOf(lowerSearchTerm, startIndex);
        if (matchIndex == -1) break;

        // Verificar que es una coincidencia exacta (no parte de otra palabra)
        final bool isExactMatch =
            true; // Siempre es exacta porque buscamos la cadena completa

        if (isExactMatch) {
          if (kDebugMode) {
            final String matchedText = paragraph.plainText
                .substring(matchIndex, matchIndex + lowerSearchTerm.length);
            print(
                '✓ Coincidencia encontrada en párrafo ${paragraph.id}: "$matchedText"');
          }

          _searchMatches.add(SearchMatch(
            paragraphIndex: i,
            paragraphId: paragraph.id,
            startIndex: matchIndex,
            endIndex: matchIndex + lowerSearchTerm.length,
            matchText: paragraph.plainText
                .substring(matchIndex, matchIndex + lowerSearchTerm.length),
          ));
        }

        startIndex = matchIndex + lowerSearchTerm.length;
      }
    }

    if (kDebugMode) {
      print(
          '🔍 Se encontraron ${_searchMatches.length} coincidencias exactas en ${_paragraphs.length} párrafos');

      // Imprimir detalles de las coincidencias para depuración
      for (int i = 0; i < _searchMatches.length; i++) {
        final match = _searchMatches[i];
        print('  ${i + 1}. Párrafo ${match.paragraphId}: "${match.matchText}"');
      }
    }

    // Actualizar el estado para reflejar los resultados
    setState(() {
      if (_searchMatches.isNotEmpty) {
        currentResultIndex = 0;
      } else {
        currentResultIndex = -1;
        if (!_hasShownNotification) {
          _showNotification();
          _hasShownNotification = true;
        }
      }
    });

    // Calcular las posiciones de los resultados
    if (_searchMatches.isNotEmpty) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _findSearchResultPositions();
        _navigateToSearchResult(0);
      });
    }
  }

  // Método para encontrar las posiciones de los resultados de búsqueda
  void _findSearchResultPositions() {
    if (_searchMatches.isEmpty) return;

    // Obtener el contexto del contenedor HTML
    final BuildContext? htmlContext = _htmlContainerKey.currentContext;
    if (htmlContext == null) {
      if (kDebugMode) {
        print('⚠️ No se pudo obtener el contexto del contenedor HTML');
      }
      return;
    }

    // Obtener el RenderBox del contenedor HTML
    final RenderBox? htmlBox = htmlContext.findRenderObject() as RenderBox?;
    if (htmlBox == null) {
      if (kDebugMode) {
        print('⚠️ No se pudo obtener el RenderBox del contenedor HTML');
      }
      return;
    }

    // Actualizar las posiciones de los resultados
    for (int i = 0; i < _searchMatches.length; i++) {
      final match = _searchMatches[i];
      final paragraphKey = _paragraphKeys[match.paragraphId];

      if (paragraphKey?.currentContext != null) {
        final RenderBox box =
            paragraphKey!.currentContext!.findRenderObject() as RenderBox;
        final Offset position = box.localToGlobal(Offset.zero);

        // Actualizar la posición del resultado
        _searchMatches[i] = SearchMatch(
          paragraphIndex: match.paragraphIndex,
          paragraphId: match.paragraphId,
          startIndex: match.startIndex,
          endIndex: match.endIndex,
          matchText: match.matchText,
          rect: Rect.fromLTWH(
            20, // Margen izquierdo aproximado
            position.dy + 10, // Posición vertical aproximada
            box.size.width - 40, // Ancho aproximado
            24, // Alto aproximado
          ),
        );
      }
    }

    // Actualizar el estado
    setState(() {});
  }

  // Método para navegar a un resultado específico
  void _navigateToSearchResult(int index) {
    if (index < 0 || index >= _searchMatches.length) return;

    final match = _searchMatches[index];
    final paragraphKey = _paragraphKeys[match.paragraphId];

    if (kDebugMode) {
      print(
          '🔍 Navegando al resultado #${index + 1}: "${match.matchText}" en párrafo ${match.paragraphId}');
    }

    if (paragraphKey?.currentContext != null) {
      // Actualizar el índice actual antes de desplazarse
      setState(() {
        currentResultIndex = index;
        _searchButtonController.value = '${index + 1}/${_searchMatches.length}';
      });

      // Dar tiempo para que se actualice la UI
      Future.delayed(const Duration(milliseconds: 50), () {
        if (mounted) {
          Scrollable.ensureVisible(
            paragraphKey!.currentContext!,
            alignment:
                0.3, // Posicionar el párrafo en el tercio superior de la pantalla
            duration: const Duration(milliseconds: 300),
          );
        }
      });
    } else {
      if (kDebugMode) {
        print(
            '⚠️ No se pudo encontrar el contexto para el párrafo ${match.paragraphId}');
      }

      // Intentar actualizar las posiciones y volver a intentar
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _findSearchResultPositions();

        // Intentar nuevamente después de actualizar las posiciones
        Future.delayed(const Duration(milliseconds: 100), () {
          if (mounted && paragraphKey?.currentContext != null) {
            setState(() {
              currentResultIndex = index;
              _searchButtonController.value =
                  '${index + 1}/${_searchMatches.length}';
            });

            Scrollable.ensureVisible(
              paragraphKey!.currentContext!,
              alignment: 0.3,
              duration: const Duration(milliseconds: 300),
            );
          }
        });
      });
    }
  }

  // Método para ir al siguiente resultado
  void nextResult() {
    if (_searchMatches.isEmpty) return;

    final nextIndex = (currentResultIndex + 1) % _searchMatches.length;
    _navigateToSearchResult(nextIndex);
  }

  // Método para ir al resultado anterior
  void previousResult() {
    if (_searchMatches.isEmpty) return;

    final prevIndex = (currentResultIndex - 1 + _searchMatches.length) %
        _searchMatches.length;
    _navigateToSearchResult(prevIndex);
  }

  // Método para resaltar texto en un párrafo
  Widget _buildHighlightedParagraph(ParagraphData paragraph) {
    // Si no hay búsqueda activa, mostrar el párrafo normal
    if (_searchMatches.isEmpty ||
        widget.searchTerm == null ||
        widget.searchTerm!.isEmpty) {
      return HtmlWidget(
        paragraph.content,
        key: _paragraphKeys[paragraph.id],
        textStyle: const TextStyle(
          fontSize: 16,
          height: 1.5,
          fontFamily: 'Arial Narrow',
        ),
        customStylesBuilder: (element) {
          return {
            'margin': '0',
            'padding': '0',
            'line-height': '1.5',
          };
        },
      );
    }

    // Buscar coincidencias en este párrafo
    final List<SearchMatch> matchesInParagraph = _searchMatches
        .where((match) => match.paragraphId == paragraph.id)
        .toList();

    if (matchesInParagraph.isEmpty) {
      // Si no hay coincidencias en este párrafo, mostrar el contenido normal
      return HtmlWidget(
        paragraph.content,
        key: _paragraphKeys[paragraph.id],
        textStyle: const TextStyle(
          fontSize: 16,
          height: 1.5,
          fontFamily: 'Arial Narrow',
        ),
        customStylesBuilder: (element) {
          return {
            'margin': '0',
            'padding': '0',
            'line-height': '1.5',
          };
        },
      );
    }

    // Hay coincidencias, crear HTML con resaltado
    String highlightedContent = paragraph.content;

    try {
      // Reemplazar el contenido del párrafo con versiones resaltadas
      final String searchTerm = widget.searchTerm!;

      // Escapar caracteres especiales en el término de búsqueda para la regex
      final String escapedSearchTerm = RegExp.escape(searchTerm);

      // Crear una expresión regular que evite reemplazar dentro de etiquetas HTML
      final regex = RegExp(
        '(?<!<[^>]*?)($escapedSearchTerm)(?![^<]*?>)',
        caseSensitive: false,
      );

      // Reemplazar todas las coincidencias con spans resaltados
      highlightedContent = highlightedContent.replaceAllMapped(regex, (match) {
        // Determinar si esta coincidencia es la actual
        final bool isCurrentMatch = matchesInParagraph.any((m) =>
            _searchMatches.indexOf(m) == currentResultIndex &&
            paragraph.plainText
                    .toLowerCase()
                    .indexOf(match[0]!.toLowerCase(), m.startIndex) ==
                m.startIndex);

        // Usar colores diferentes para la coincidencia actual vs otras coincidencias
        final String highlightColor = isCurrentMatch ? '#ffa500' : '#ffff00';
        final String opacity = isCurrentMatch ? '1.0' : '0.7';

        // Crear el span con el estilo adecuado
        return '<span style="background-color: $highlightColor; opacity: $opacity; display: inline; padding: 2px; border-radius: 4px;">${match[0]}</span>';
      });

      if (kDebugMode) {
        print(
            '✅ Párrafo ${paragraph.id} resaltado con ${matchesInParagraph.length} coincidencias');
      }
    } catch (e) {
      if (kDebugMode) {
        print('❌ Error al resaltar párrafo ${paragraph.id}: $e');
      }
      // En caso de error, mostrar el contenido original
      return HtmlWidget(
        paragraph.content,
        key: _paragraphKeys[paragraph.id],
        textStyle: const TextStyle(
          fontSize: 16,
          height: 1.5,
          fontFamily: 'Arial Narrow',
        ),
        customStylesBuilder: (element) {
          return {
            'margin': '0',
            'padding': '0',
            'line-height': '1.5',
          };
        },
      );
    }

    return HtmlWidget(
      highlightedContent,
      key: _paragraphKeys[paragraph.id],
      textStyle: const TextStyle(
        fontSize: 16,
        height: 1.5,
        fontFamily: 'Arial Narrow',
      ),
      customStylesBuilder: (element) {
        if (element.localName == 'span' &&
            element.attributes.containsKey('style') &&
            element.attributes['style']!.contains('background-color')) {
          return {
            'display': 'inline',
            'padding': '2px',
            'border-radius': '2px',
          };
        }
        return {
          'margin': '0',
          'padding': '0',
          'line-height': '1.5',
        };
      },
    );
  }

  void _showImage() {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ImageViewerScreen(
          imagePath: thumbnail,
          onDownload: () async {
            final hasAccess = await Gal.hasAccess();
            if (!hasAccess) {
              await Gal.requestAccess();
            } else {
              try {
                await Gal.putImage(thumbnail,
                    album: 'La Gran Carpa Catedral Corp.');
                InAppNotifications.show(
                  title: 'image_saved'.tr(),
                  description: 'image_saved_desc'.tr(),
                  leading: const Icon(Icons.photo_library_rounded),
                  duration: const Duration(seconds: 3),
                );
              } catch (e) {
                InAppNotifications.show(
                  title: 'error_saving_image'.tr(),
                  description: 'error_saving_image_desc'.tr(),
                  leading: const Icon(Icons.error_outline),
                  duration: const Duration(seconds: 3),
                );
              }
            }
          },
        ),
      ),
    );
  }

  void scrollToTop() {
    _scrollController.animateTo(0,
        duration: const Duration(milliseconds: 500),
        curve: Curves.fastOutSlowIn);
  }

  double _calculateHeight(BuildContext context) {
    final isMobile = Platform.isAndroid || Platform.isIOS;
    final isLandscape =
        MediaQuery.of(context).orientation == Orientation.landscape;

    if (isMobile) {
      return isLandscape
          ? MediaQuery.of(context).size.height * 0.8
          : MediaQuery.of(context).size.height * 0.5;
    } else {
      return MediaQuery.of(context).size.height;
    }
  }

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

    try {
      return CountryCodes.detailsFromAlpha2(countryCode).name.toString();
    } catch (e) {
      return '';
    }
  }

  void _toggleFabMenu() {
    final state = _fabKey.currentState;
    if (state != null) {
      state.toggle();
    }
  }

  void _closeFabMenu() {
    final state = _fabKey.currentState;
    if (state != null && state.isOpen) {
      state.toggle();
    }
  }

  void _openFabMenu() {
    final state = _fabKey.currentState;
    if (state != null && !state.isOpen) {
      state.toggle();
    }
  }

  @override
  Widget build(BuildContext context) {
    final bottomPadding = !isLoadingBody && messageBody.isNotEmpty && !searching
        ? MediaQuery.of(context).size.height * 0.12 +
            MediaQuery.of(context).padding.bottom
        : 16.0 + MediaQuery.of(context).padding.bottom;

    final isLandscape =
        MediaQuery.of(context).orientation == Orientation.landscape;
    final isWideDevice = MediaQuery.of(context).size.width > 600;
    final shouldUseWideLayout = isLandscape || isWideDevice;

    final horizontalPadding = shouldUseWideLayout ? 40.0 : 16.0;
    final textScaleFactor = shouldUseWideLayout ? 1.2 : 1.0;

    return Scaffold(
      backgroundColor: const Color(0xFFf1f5eb),
      floatingActionButtonLocation: ExpandableFab.location,
      floatingActionButton: AnimatedSlide(
        duration: const Duration(milliseconds: 200),
        offset: _isSearchExpanded ? const Offset(2, 0) : Offset.zero,
        child: AnimatedOpacity(
          duration: const Duration(milliseconds: 200),
          opacity: _isSearchExpanded ? 0.0 : 1.0,
          child: !isLoadingBody && messageBody.isNotEmpty && !searching
              ? ExpandableFab(
                  key: _fabKey,
                  distance: 60,
                  type: ExpandableFabType.up,
                  closeButtonBuilder: FloatingActionButtonBuilder(
                    size: 40,
                    builder: (BuildContext context, void Function()? onPressed,
                        Animation<double> progress) {
                      return FloatingActionButton.small(
                        heroTag: 'close_fab',
                        onPressed: () {
                          _closeFabMenu();
                        },
                        backgroundColor: Colors.white,
                        foregroundColor: const Color(0xFF6b8e23),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(8),
                        ),
                        child: const Icon(Icons.close),
                      );
                    },
                  ),
                  openButtonBuilder: DefaultFloatingActionButtonBuilder(
                    child: const Icon(Icons.menu),
                    backgroundColor: const Color(0xFF6b8e23),
                    foregroundColor: Colors.white,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(8),
                    ),
                    fabSize: ExpandableFabSize.small,
                  ),
                  pos: ExpandableFabPos.right,
                  duration: const Duration(milliseconds: 150),
                  children: [
                    FloatingActionButton.small(
                      heroTag: 'scroll_top',
                      onPressed: scrollToTop,
                      backgroundColor: const Color(0xFF6b8e23).withAlpha(200),
                      child: const Icon(Icons.keyboard_arrow_up),
                    ),
                    FloatingActionButton.small(
                      heroTag: 'search',
                      onPressed: () {
                        setState(() {
                          _isSearchExpanded = true;
                          _isMenuOpen = false;
                        });
                      },
                      backgroundColor: const Color(0xFF6b8e23).withAlpha(200),
                      child: const Icon(Icons.search),
                    ),
                  ],
                )
              : null,
        ),
      ),
      body: Stack(
        children: [
          SingleChildScrollView(
            controller: _scrollController,
            padding: EdgeInsets.only(
                bottom: MediaQuery.of(context).viewInsets.bottom + 80,
                top: 16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Stack(
                  children: [
                    Container(
                      constraints:
                          const BoxConstraints(maxHeight: 400, minHeight: 200),
                      height: _calculateHeight(context),
                      width: double.infinity,
                      child: ClipRRect(
                        borderRadius: const BorderRadius.only(
                          bottomLeft: Radius.circular(16),
                          bottomRight: Radius.circular(16),
                        ),
                        child: FutureBuilder<File?>(
                          future: thumbnail.isNotEmpty
                              ? Future.value(File(thumbnail))
                              : _getThumbnail(widget.data.thumbnail),
                          builder: (context, snapshot) {
                            return Skeletonizer(
                              effect: const ShimmerEffect(
                                baseColor: Color(0xFFf1f5eb),
                                highlightColor: Colors.white30,
                                duration: Duration(milliseconds: 1000),
                              ),
                              enableSwitchAnimation: true,
                              enabled: isLoading,
                              child: InkWell(
                                onTap: () => _showImage(),
                                child: Image(
                                  image: (snapshot.hasData &&
                                          snapshot.data != null)
                                      ? FileImage(snapshot.data!)
                                      : const AssetImage(
                                              'assets/image/default_thumbnail.jpg')
                                          as ImageProvider,
                                  alignment: Alignment.topCenter,
                                  fit: BoxFit.cover,
                                  width: MediaQuery.of(context).size.width,
                                  height: _calculateHeight(context),
                                ),
                              ),
                            );
                          },
                        ),
                      ),
                    ),
                    Positioned(
                      left: 16,
                      top: MediaQuery.of(context).size.height * 0.05,
                      child: IconButton(
                          icon: const Icon(Icons.arrow_back),
                          color: Colors.white,
                          onPressed: () => Navigator.pop(context),
                          tooltip: 'back'.tr(),
                          style: IconButton.styleFrom(
                            backgroundColor:
                                const Color(0xFF6b8e23).withAlpha(200),
                            padding: const EdgeInsets.all(10),
                            iconSize: 18,
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(10),
                            ),
                          )),
                    ),
                    Positioned(
                      right: 18,
                      top: MediaQuery.of(context).size.height * 0.05,
                      child: IconButton(
                          icon: Icon(
                              isFav ? Icons.bookmark : Icons.bookmark_border,
                              color: Colors.white),
                          onPressed: () => {_toggleFavorite()},
                          tooltip: 'favorite'.tr(),
                          style: IconButton.styleFrom(
                            backgroundColor: isFav
                                ? const Color(0xFF6b8e23).withAlpha(200)
                                : Colors.black38,
                            padding: const EdgeInsets.all(10),
                            iconSize: 18,
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(10),
                            ),
                          )),
                    ),
                    if (downloading &&
                        downloadProgress > 0 &&
                        downloadProgress < 100)
                      Positioned(
                        right: 72,
                        top: MediaQuery.of(context).size.height * 0.05,
                        child: SizedBox(
                          width: 48,
                          height: 48,
                          child: CircularPercentIndicator(
                            backgroundColor: Colors.grey.shade400,
                            progressColor: const Color(0xFF6b8e23),
                            lineWidth: 4,
                            radius: 20,
                            center: Text(downloadProgress.toString(),
                                style: TextStyle(
                                  fontSize: 14,
                                  color: Colors.black,
                                  shadows: [
                                    Shadow(
                                      color: Colors.black.withAlpha(51),
                                      offset: const Offset(0, 1),
                                      blurRadius: 2,
                                    ),
                                  ],
                                )),
                            percent: downloadProgress / 100,
                          ),
                        ),
                      ),
                    Positioned(
                      left: shouldUseWideLayout ? 40 : 16,
                      bottom: 16,
                      right: shouldUseWideLayout ? 40 : 16,
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(8),
                        child: BackdropFilter(
                          filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
                          child: Container(
                            padding: const EdgeInsets.symmetric(
                                horizontal: 12, vertical: 8),
                            decoration: BoxDecoration(
                              color: Colors.black.withAlpha(128),
                              borderRadius: BorderRadius.circular(8),
                            ),
                            child: Text(
                              widget.data.title.isNotEmpty
                                  ? widget.data.title
                                  : utils.formatDate(widget.data.date, locale),
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 24,
                                fontWeight: FontWeight.bold,
                                height: 1.2,
                                shadows: [
                                  Shadow(
                                    color: Colors.black.withAlpha(51),
                                    offset: const Offset(0, 1),
                                    blurRadius: 2,
                                  ),
                                ],
                              ),
                            ),
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10),
                          color: const Color(0xFFe0e6d1),
                        ),
                        width: double.infinity,
                        child: Padding(
                          padding: const EdgeInsets.all(16.0),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              Flexible(
                                child: Container(
                                  constraints: BoxConstraints(
                                      maxWidth: (widget.data.pdf != null &&
                                              widget.data.pdf!.isNotEmpty)
                                          ? MediaQuery.of(context).size.width *
                                              0.6
                                          : MediaQuery.of(context).size.width),
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                          utils.formatDate(
                                              widget.data.date, locale),
                                          style: const TextStyle(fontSize: 16)),
                                      if (widget.data.city.isNotEmpty ||
                                          widget.data.country.isNotEmpty)
                                        Text(
                                          widget.data.city.isNotEmpty
                                              ? '${widget.data.city}, ${_getCountryName(widget.data.country)}'
                                              : _getCountryName(
                                                  widget.data.country),
                                          style: const TextStyle(
                                            color: Colors.black87,
                                            fontSize: 16,
                                          ),
                                        ),
                                      Text(
                                        '${plural('activity', 1)} ${widget.data.activity}',
                                        style: const TextStyle(
                                          color: Colors.black87,
                                          fontSize: 16,
                                          overflow: TextOverflow.ellipsis,
                                        ),
                                        maxLines: 1,
                                      ),
                                    ],
                                  ),
                                ),
                              ),
                              Row(
                                children: [
                                  if (widget.data.pdf != null &&
                                      widget.data.pdf!.isNotEmpty)
                                    IconButton(
                                      icon: const Icon(Icons.picture_as_pdf),
                                      onPressed: () {
                                        Navigator.push(
                                          context,
                                          MaterialPageRoute(
                                            builder: (context) => FilePdf(
                                                pdf: widget.data.pdf!,
                                                title:
                                                    widget.data.title.isNotEmpty
                                                        ? widget.data.title
                                                        : utils.formatDate(
                                                            widget.data.date,
                                                            locale),
                                                searchTerm:
                                                    _searchController.text),
                                          ),
                                        );
                                      },
                                    ),
                                ],
                              ),
                            ],
                          ),
                        ),
                      ),
                      const SizedBox(height: 16),
                      if (isLoadingBody)
                        Skeletonizer(
                          effect: const ShimmerEffect(
                            baseColor: Color(0xFFf1f5eb),
                            highlightColor: Colors.white30,
                            duration: Duration(milliseconds: 1000),
                          ),
                          enabled: true,
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                              Container(
                                width: double.infinity,
                                height: 24,
                                margin: const EdgeInsets.only(bottom: 12),
                                decoration: BoxDecoration(
                                  color: const Color(0xFFe0e6d1),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                              ),
                            ],
                          ),
                        ),
                      if (!isLoadingBody && messageBody.isNotEmpty)
                        Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 8.0),
                          child: Stack(
                            children: [
                              // Contenido HTML principal
                              SelectionArea(
                                child: Container(
                                  key: _htmlContainerKey,
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: _paragraphs.map((paragraph) {
                                      return Padding(
                                        padding:
                                            const EdgeInsets.only(bottom: 16.0),
                                        child: _buildHighlightedParagraph(
                                            paragraph),
                                      );
                                    }).toList(),
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      if (!isLoadingBody && messageBody.isEmpty)
                        Center(
                          child: Padding(
                            padding: const EdgeInsets.all(20.0),
                            child: Text(
                              'no_text'.tr(),
                              style: TextStyle(
                                fontSize: 16 * textScaleFactor,
                                color: Colors.black87,
                              ),
                            ),
                          ),
                        ),
                    ],
                  ),
                ),
              ],
            ),
          ),
          if (!isLoadingBody && messageBody.isNotEmpty && !searching)
            Positioned(
              left: 0,
              right: 0,
              bottom: MediaQuery.of(context).viewPadding.bottom + 16,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  AnimatedSlide(
                    duration: const Duration(milliseconds: 200),
                    offset:
                        _isSearchExpanded ? Offset.zero : const Offset(0, 2),
                    child: AnimatedOpacity(
                      duration: const Duration(milliseconds: 200),
                      opacity: _isSearchExpanded ? 1.0 : 0.0,
                      child: _isSearchExpanded
                          ? Padding(
                              padding:
                                  const EdgeInsets.symmetric(horizontal: 16),
                              child: SearchBar(
                                searchController: _searchController,
                                previousResult: previousResult,
                                nextResult: nextResult,
                                onClose: () {
                                  setState(() {
                                    _isSearchExpanded = false;
                                    _searchController.clear();
                                    widget.searchTerm = '';
                                    highlightedHtml = '';
                                    _searchMatches = [];
                                    currentResultIndex = -1;
                                  });
                                },
                                onChanged: (val) {
                                  _onSearch();
                                },
                                hasResults: _searchMatches.isNotEmpty,
                                currentResultIndex: currentResultIndex,
                                totalResults: _searchMatches.length,
                              ),
                            )
                          : const SizedBox(),
                    ),
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }
}

class SearchBar extends StatelessWidget {
  final TextEditingController searchController;
  final VoidCallback previousResult;
  final VoidCallback nextResult;
  final VoidCallback onClose;
  final ValueChanged<String> onChanged;
  final bool hasResults;
  final int currentResultIndex;
  final int totalResults;

  const SearchBar({
    super.key,
    required this.searchController,
    required this.previousResult,
    required this.nextResult,
    required this.onChanged,
    required this.onClose,
    required this.hasResults,
    required this.currentResultIndex,
    required this.totalResults,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: const Color(0xFFe0e6d1),
        borderRadius: BorderRadius.circular(10),
      ),
      child: TextFormField(
        decoration: InputDecoration(
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(10),
            borderSide: BorderSide.none,
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(10),
            borderSide: BorderSide.none,
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(10),
            borderSide: BorderSide.none,
          ),
          hintText: 'search_placeholder'.tr(),
          hintStyle: const TextStyle(color: Color(0xFF9E9E9E)),
          contentPadding:
              const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
          suffixIcon: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              if (hasResults) ...[
                IconButton(
                  icon: const Icon(Icons.keyboard_arrow_left),
                  color: const Color(0xFF6b8e22),
                  onPressed: previousResult,
                ),
                Text(
                  '${currentResultIndex + 1}/$totalResults',
                  style: const TextStyle(
                    color: Color(0xFF6b8e22),
                    fontSize: 12,
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.keyboard_arrow_right),
                  color: const Color(0xFF6b8e22),
                  onPressed: nextResult,
                ),
                const SizedBox(width: 8),
              ],
              IconButton(
                icon: const Icon(Icons.close),
                color: const Color(0xFF6b8e22),
                onPressed: onClose,
              ),
            ],
          ),
        ),
        controller: searchController,
        onFieldSubmitted: (_) => onChanged(''),
        autofocus: false,
        autocorrect: true,
      ),
    );
  }
}

enum HtmlTag {
  p,
  span,
  text,
  mark,
  a,
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  strong,
  em,
  i,
  b,
  u,
  br,
  ul,
  ol,
  li,
  blockquote,
  pre,
  code,
  img,
  hr,
  sup,
  sub,
  small,
  big,
  center,
  font,
}

extension HtmlTagExtension on HtmlTag {
  String get name {
    switch (this) {
      case HtmlTag.p:
        return 'p';
      case HtmlTag.span:
        return 'span';
      case HtmlTag.text:
        return 'text';
      case HtmlTag.mark:
        return 'mark';
      case HtmlTag.a:
        return 'a';
      case HtmlTag.h1:
        return 'h1';
      case HtmlTag.h2:
        return 'h2';
      case HtmlTag.h3:
        return 'h3';
      case HtmlTag.h4:
        return 'h4';
      case HtmlTag.h5:
        return 'h5';
      case HtmlTag.h6:
        return 'h6';
      case HtmlTag.strong:
        return 'strong';
      case HtmlTag.em:
        return 'em';
      case HtmlTag.i:
        return 'i';
      case HtmlTag.b:
        return 'b';
      case HtmlTag.u:
        return 'u';
      case HtmlTag.br:
        return 'br';
      case HtmlTag.ul:
        return 'ul';
      case HtmlTag.ol:
        return 'ol';
      case HtmlTag.li:
        return 'li';
      case HtmlTag.blockquote:
        return 'blockquote';
      case HtmlTag.pre:
        return 'pre';
      case HtmlTag.code:
        return 'code';
      case HtmlTag.img:
        return 'img';
      case HtmlTag.hr:
        return 'hr';
      case HtmlTag.sup:
        return 'sup';
      case HtmlTag.sub:
        return 'sub';
      case HtmlTag.small:
        return 'small';
      case HtmlTag.big:
        return 'big';
      case HtmlTag.center:
        return 'center';
      case HtmlTag.font:
        return 'font';
    }
  }

  static HtmlTag? fromString(String name) {
    return HtmlTag.values.firstWhere(
      (tag) => tag.name == name.toLowerCase(),
      orElse: () => HtmlTag.text,
    );
  }
}

class OptimizedHtmlRenderer extends StatefulWidget {
  final String html;
  final TextStyle? textStyle;
  final VoidCallback? onRendered;
  final Function(List<SearchResultInfo>)? onSearchResultsFound;
  final String? searchTerm;
  final int? currentResultIndex;

  const OptimizedHtmlRenderer({
    super.key,
    required this.html,
    this.textStyle = const TextStyle(
      fontSize: 16,
      height: 1.5,
      fontFamily: 'Arial Narrow',
    ),
    this.onRendered,
    this.onSearchResultsFound,
    this.searchTerm,
    this.currentResultIndex,
  });

  @override
  State<OptimizedHtmlRenderer> createState() => _OptimizedHtmlRendererState();
}

class _OptimizedHtmlRendererState extends State<OptimizedHtmlRenderer> {
  // Lista de resultados de búsqueda
  List<SearchResultInfo> _searchResults = [];

  // Mapa para almacenar las claves de los elementos renderizados
  final Map<String, GlobalKey> _elementKeys = {};

  // HTML procesado
  String _processedHtml = '';

  // Controlador de scroll
  final ScrollController _scrollController = ScrollController();

  // Estado de carga
  bool _isLoading = true;

  // Clave global para el contenedor
  final GlobalKey _containerKey = GlobalKey();

  // Último término de búsqueda procesado
  String? _lastSearchTerm;

  // Timer para actualizar posiciones
  Timer? _positionUpdateTimer;

  @override
  void initState() {
    super.initState();
    _processHtml();
  }

  @override
  void didUpdateWidget(OptimizedHtmlRenderer oldWidget) {
    super.didUpdateWidget(oldWidget);

    // Si el HTML cambió, volver a procesarlo
    if (oldWidget.html != widget.html) {
      _processHtml();
    }

    // Si el término de búsqueda cambió, actualizar los resultados
    if (oldWidget.searchTerm != widget.searchTerm) {
      // Limpiar resultados anteriores si el término está vacío
      if (widget.searchTerm == null || widget.searchTerm!.isEmpty) {
        setState(() {
          _searchResults = [];
        });
        if (widget.onSearchResultsFound != null) {
          widget.onSearchResultsFound!([]);
        }
      } else if (_lastSearchTerm != widget.searchTerm) {
        _lastSearchTerm = widget.searchTerm;
        _updateSearchResults();
      }
    }

    // Si el índice actual cambió, actualizar el resaltado
    if (oldWidget.currentResultIndex != widget.currentResultIndex &&
        widget.currentResultIndex != null) {
      _scrollToResult(widget.currentResultIndex!);
    }
  }

  // Procesar el HTML
  Future<void> _processHtml() async {
    setState(() {
      _isLoading = true;
      // Limpiar resultados anteriores al procesar nuevo HTML
      _searchResults = [];
      _elementKeys.clear();
      _lastSearchTerm = null;
    });

    try {
      // Procesar el HTML en un isolate
      final result = await compute(_processHtmlInIsolate, {
        'html': widget.html,
        'searchTerm': widget.searchTerm ?? '',
      });

      if (!mounted) return;

      setState(() {
        _processedHtml = result['html'] as String;
        _isLoading = false;
      });

      // Notificar que el HTML se ha renderizado
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (widget.onRendered != null) {
          widget.onRendered!();
        }

        // Actualizar los resultados de búsqueda
        if (widget.searchTerm != null && widget.searchTerm!.isNotEmpty) {
          _lastSearchTerm = widget.searchTerm;
          _updateSearchResults();
        }
      });
    } catch (e) {
      if (kDebugMode) {
        print('❌ Error procesando HTML: $e');
      }

      setState(() {
        _processedHtml = widget.html;
        _isLoading = false;
      });
    }
  }

  // Actualizar los resultados de búsqueda
  void _updateSearchResults() {
    // Cancelar cualquier timer anterior
    _positionUpdateTimer?.cancel();

    if (widget.searchTerm == null || widget.searchTerm!.isEmpty) {
      setState(() {
        _searchResults = [];
      });
      if (widget.onSearchResultsFound != null) {
        widget.onSearchResultsFound!([]);
      }
      return;
    }

    // Limpiar resultados anteriores
    setState(() {
      _searchResults = [];
    });

    // Buscar los resultados en el DOM después de que se haya renderizado
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // Dar tiempo para que el DOM se actualice completamente
      _positionUpdateTimer = Timer(const Duration(milliseconds: 200), () {
        if (mounted) {
          _findSearchResultsInDom();

          // Programar actualizaciones periódicas de las posiciones
          // para manejar cambios en el layout
          _positionUpdateTimer =
              Timer.periodic(const Duration(seconds: 1), (timer) {
            if (mounted && _searchResults.isNotEmpty) {
              _updateSearchResultPositions();
            } else {
              timer.cancel();
            }
          });
        }
      });
    });
  }

  // Encontrar los resultados de búsqueda en el DOM
  void _findSearchResultsInDom() {
    // Limpiar resultados anteriores
    List<SearchResultInfo> newResults = [];

    // Obtener el contexto del contenedor
    final BuildContext? containerContext = _containerKey.currentContext;
    if (containerContext == null) {
      if (kDebugMode) {
        print('⚠️ No se pudo obtener el contexto del contenedor');
      }
      return;
    }

    // Obtener el RenderBox del contenedor
    final RenderBox? containerBox =
        containerContext.findRenderObject() as RenderBox?;
    if (containerBox == null || !containerBox.hasSize) {
      if (kDebugMode) {
        print('⚠️ No se pudo obtener el RenderBox del contenedor');
      }
      return;
    }

    // Buscar elementos con la clase 'search-result'
    for (final entry in _elementKeys.entries) {
      if (entry.key.startsWith('search-result-')) {
        final key = entry.value;
        final context = key.currentContext;

        if (context != null) {
          final renderBox = context.findRenderObject() as RenderBox?;
          if (renderBox != null && renderBox.hasSize) {
            // Calcular la posición relativa al contenedor
            final Offset position =
                renderBox.localToGlobal(Offset.zero, ancestor: containerBox);
            final size = renderBox.size;

            // Extraer el índice del ID
            final index = int.tryParse(entry.key.split('-').last) ?? 0;

            newResults.add(SearchResultInfo(
              id: entry.key,
              index: index,
              rect: Rect.fromLTWH(
                position.dx,
                position.dy,
                size.width,
                size.height,
              ),
              key: key,
            ));
          }
        }
      }
    }

    // Ordenar los resultados por índice
    newResults.sort((a, b) => a.index.compareTo(b.index));

    if (kDebugMode) {
      print('🔍 Se encontraron ${newResults.length} resultados de búsqueda');
    }

    // Actualizar el estado con los nuevos resultados
    setState(() {
      _searchResults = newResults;
    });

    // Notificar los resultados
    if (widget.onSearchResultsFound != null) {
      widget.onSearchResultsFound!(newResults);
    }

    // Si hay un índice actual, resaltar ese resultado
    if (widget.currentResultIndex != null &&
        widget.currentResultIndex! < newResults.length) {
      _scrollToResult(widget.currentResultIndex!);
    }
  }

  // Actualizar las posiciones de los resultados de búsqueda
  void _updateSearchResultPositions() {
    if (_searchResults.isEmpty) return;

    // Obtener el contexto del contenedor
    final BuildContext? containerContext = _containerKey.currentContext;
    if (containerContext == null) return;

    // Obtener el RenderBox del contenedor
    final RenderBox? containerBox =
        containerContext.findRenderObject() as RenderBox?;
    if (containerBox == null || !containerBox.hasSize) return;

    // Lista temporal para los resultados actualizados
    List<SearchResultInfo> updatedResults = [];
    bool positionsChanged = false;

    // Actualizar cada resultado
    for (final result in _searchResults) {
      final context = result.key.currentContext;
      if (context != null) {
        final renderBox = context.findRenderObject() as RenderBox?;
        if (renderBox != null && renderBox.hasSize) {
          // Calcular la nueva posición
          final Offset position =
              renderBox.localToGlobal(Offset.zero, ancestor: containerBox);
          final size = renderBox.size;

          final Rect newRect = Rect.fromLTWH(
            position.dx,
            position.dy,
            size.width,
            size.height,
          );

          // Verificar si la posición cambió
          if (newRect != result.rect) {
            positionsChanged = true;
            updatedResults.add(SearchResultInfo(
              id: result.id,
              index: result.index,
              rect: newRect,
              key: result.key,
            ));
          } else {
            updatedResults.add(result);
          }
        } else {
          updatedResults.add(result);
        }
      } else {
        updatedResults.add(result);
      }
    }

    // Actualizar el estado solo si las posiciones cambiaron
    if (positionsChanged && mounted) {
      setState(() {
        _searchResults = updatedResults;
      });

      // Notificar los resultados actualizados
      if (widget.onSearchResultsFound != null) {
        widget.onSearchResultsFound!(updatedResults);
      }
    }
  }

  // Hacer scroll a un resultado específico
  void _scrollToResult(int index) {
    if (_searchResults.isEmpty || index >= _searchResults.length) return;

    final result = _searchResults[index];
    final context = result.key.currentContext;

    if (context != null) {
      Scrollable.ensureVisible(
        context,
        alignment: 0.5,
        duration: const Duration(milliseconds: 300),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Center(
        child: CircularProgressIndicator(
          strokeWidth: 2,
          color: Color(0xFF6b8e23),
        ),
      );
    }

    return Stack(
      key: _containerKey,
      children: [
        // Contenido HTML base
        HtmlWidget(
          _processedHtml,
          textStyle: widget.textStyle,
          customWidgetBuilder: (element) {
            // Crear claves para los elementos de búsqueda
            if (element.localName == 'span' &&
                element.attributes.containsKey('id') &&
                element.attributes['id']!.startsWith('search-result-')) {
              final id = element.attributes['id']!;
              _elementKeys.putIfAbsent(id, () => GlobalKey());
            }
            return null; // Dejar que el widget predeterminado maneje el renderizado
          },
          customStylesBuilder: (element) {
            // Estilos base para los elementos, sin resaltado
            if (element.localName == 'p' || element.localName == 'div') {
              return {
                'margin': '0',
                'padding': '0 0 16px 0',
                'font-family': 'Arial Narrow !important',
              };
            }

            // Forzar Arial Narrow en todos los elementos que puedan tener texto
            if (element.localName == 'span' ||
                element.localName == 'h1' ||
                element.localName == 'h2' ||
                element.localName == 'h3' ||
                element.localName == 'h4' ||
                element.localName == 'h5' ||
                element.localName == 'h6' ||
                element.localName == 'strong' ||
                element.localName == 'em' ||
                element.localName == 'i' ||
                element.localName == 'b' ||
                element.localName == 'u' ||
                element.localName == 'li' ||
                element.localName == 'a' ||
                element.localName == 'font') {
              return {
                'font-family': 'Arial Narrow !important',
                'margin': '0',
                'padding': '0',
              };
            }

            return {
              'font-family': 'Arial Narrow !important',
              'margin': '0',
              'padding': '0',
            };
          },
          renderMode: RenderMode.column,
          enableCaching: true,
          buildAsync: true,
          key: ValueKey('html-${widget.html.hashCode}'),
        ),

        // Overlay para los resultados de búsqueda
        if (_searchResults.isNotEmpty)
          IgnorePointer(
            child: Stack(
              children: _searchResults.map((result) {
                final bool isCurrentResult =
                    widget.currentResultIndex != null &&
                        result.index == widget.currentResultIndex;

                return Positioned(
                  left: result.rect.left,
                  top: result.rect.top,
                  width: result.rect.width,
                  height: result.rect.height,
                  child: Container(
                    decoration: BoxDecoration(
                      color: isCurrentResult
                          ? const Color(0xFFFFA500)
                              .withAlpha(128) // Naranjo para el actual
                          : const Color(0xFFFFEB3B)
                              .withAlpha(77), // Amarillo para los demás
                      borderRadius: BorderRadius.circular(2),
                    ),
                  ),
                );
              }).toList(),
            ),
          ),
      ],
    );
  }

  @override
  void dispose() {
    _scrollController.dispose();
    _positionUpdateTimer?.cancel();
    super.dispose();
  }

  // Procesar HTML en un isolate
  static Map<String, dynamic> _processHtmlInIsolate(
      Map<String, dynamic> params) {
    final String html = params['html'] as String;
    final String searchTerm = params['searchTerm'] as String? ?? '';

    if (searchTerm.isEmpty) {
      return {'html': html, 'count': 0};
    }

    try {
      // Usar un enfoque de coincidencia exacta
      String processedHtml = html;
      int matchCount = 0;

      // Escapar el término de búsqueda para la expresión regular
      final escapedSearchTerm = RegExp.escape(searchTerm);
      if (kDebugMode) {
        print('🔒 Término escapado: $escapedSearchTerm');
      }

      // Usar una expresión regular más precisa para coincidencia exacta
      // que evite reemplazar dentro de etiquetas HTML
      final regex = RegExp(
        '(?<!<[^>]*)($escapedSearchTerm)(?![^<]*>)',
        caseSensitive: false,
      );

      processedHtml = processedHtml.replaceAllMapped(regex, (match) {
        if (kDebugMode) {
          print('✨ Coincidencia exacta encontrada: ${match[0]}');
        }

        // Crear un marcado que sea compatible con flutter_widget_from_html
        // Añadir un ID único para cada coincidencia
        final result =
            '<span id="search-result-$matchCount" class="search-result">${match[0]}</span>';
        matchCount++;
        return result;
      });

      if (kDebugMode) {
        print('📊 Total de coincidencias exactas: $matchCount');
      }

      return {'html': processedHtml, 'count': matchCount};
    } catch (e) {
      if (kDebugMode) {
        print('❌ Error durante el resaltado en isolate: $e');
      }
      return {'html': html, 'count': 0};
    }
  }
}

// Clase para almacenar información sobre un resultado de búsqueda
class SearchResultInfo {
  final String id;
  final int index;
  final Rect rect;
  final GlobalKey key;

  SearchResultInfo({
    required this.id,
    required this.index,
    required this.rect,
    required this.key,
  });
}

class ImageViewerScreen extends StatelessWidget {
  final String imagePath;
  final VoidCallback onDownload;

  const ImageViewerScreen({
    super.key,
    required this.imagePath,
    required this.onDownload,
  });

  // Verificar si la imagen existe y es válida
  bool _isValidImage() {
    if (imagePath.isEmpty) return false;
    final file = File(imagePath);
    return file.existsSync() && file.lengthSync() > 0;
  }

  // Obtener el proveedor de imagen adecuado
  ImageProvider _getImageProvider() {
    if (_isValidImage()) {
      try {
        return FileImage(File(imagePath));
      } catch (e) {
        if (kDebugMode) {
          print('Error cargando imagen: $e');
        }
        return const AssetImage('assets/image/default_thumbnail.jpg');
      }
    } else {
      return const AssetImage('assets/image/default_thumbnail.jpg');
    }
  }

  @override
  Widget build(BuildContext context) {
    final imageProvider = _getImageProvider();

    return Scaffold(
      backgroundColor: Colors.transparent,
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0,
        leading: IconButton(
          icon: const Icon(Icons.arrow_back, color: Colors.white),
          onPressed: () => Navigator.pop(context),
        ),
        actions: [
          if ((Platform.isAndroid || Platform.isIOS) && _isValidImage())
            IconButton(
              icon: const Icon(Icons.download, color: Colors.white),
              onPressed: onDownload,
            ),
        ],
      ),
      body: Stack(
        fit: StackFit.expand,
        children: [
          // Imagen de fondo borrosa
          Image(
            image: imageProvider,
            fit: BoxFit.cover,
            color: Colors.black.withAlpha(128),
            colorBlendMode: BlendMode.darken,
            errorBuilder: (context, error, stackTrace) {
              if (kDebugMode) {
                print('Error mostrando imagen de fondo: $error');
              }
              return Container(color: Colors.black);
            },
          ),
          // Efecto de blur nativo
          ClipRect(
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
              child: Container(
                color: Colors.black.withAlpha(77),
              ),
            ),
          ),
          // Imagen principal
          Center(
            child: PhotoView(
              imageProvider: imageProvider,
              minScale: PhotoViewComputedScale.contained,
              maxScale: PhotoViewComputedScale.covered * 2,
              initialScale: PhotoViewComputedScale.contained,
              backgroundDecoration: const BoxDecoration(
                color: Colors.transparent,
              ),
              errorBuilder: (context, error, stackTrace) {
                if (kDebugMode) {
                  print('Error mostrando imagen principal: $error');
                }
                return Center(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      const Icon(Icons.broken_image,
                          size: 64, color: Colors.white70),
                      const SizedBox(height: 16),
                      Text(
                        'No se pudo cargar la imagen',
                        style: TextStyle(color: Colors.white70),
                      ),
                    ],
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

// Clase para almacenar información sobre un párrafo
class ParagraphData {
  final String id;
  final String content; // HTML original
  final String plainText; // Texto sin etiquetas HTML

  ParagraphData({
    required this.id,
    required this.content,
    required this.plainText,
  });
}

// Clase para almacenar información sobre una coincidencia de búsqueda
class SearchMatch {
  final int paragraphIndex;
  final String paragraphId;
  final int startIndex;
  final int endIndex;
  final String matchText;
  final Rect? rect;

  SearchMatch({
    required this.paragraphIndex,
    required this.paragraphId,
    required this.startIndex,
    required this.endIndex,
    required this.matchText,
    this.rect,
  });
}

String _removeFontFamily(String html) {
  // Remove inline styles with font-family
  html = html.replaceAll(RegExp("font-family:[^;\"']*;?"), '');
  // Remove face attributes from <font> tags
  html = html.replaceAll(RegExp("face=\"[^\"]*\""), '');
  // Remove <font> tags but keep their content
  html = html.replaceAll(RegExp("<font[^>]*>|<\\/font>"), '');
  return html;
}
