# utils/html_parser.py
import os
import re
import hashlib
from pathlib import Path
from bs4 import BeautifulSoup
from urllib.parse import urlparse, unquote
import requests
import shutil
from models.pagina_html import PaginaHTML

# Importar pypandoc para la conversión mejorada
try:
    import pypandoc
    PANDOC_AVAILABLE = True
except ImportError:
    print("⚠️ Advertencia: pypandoc no está instalado. Se utilizará html2text como fallback.")
    import html2text
    PANDOC_AVAILABLE = False

def procesar_html(ruta_archivo, dir_adjuntos):
    """
    Procesa un archivo HTML y lo convierte a Markdown, descargando
    las imágenes a la carpeta de adjuntos.
    
    Esta función mantiene la misma firma que la versión original
    para garantizar compatibilidad con el flujo existente.
    """
    # Lista de encodings para intentar
    encodings = ["utf-8", "latin-1", "iso-8859-1", "windows-1252", "cp1252"]
    
    # Lista para almacenar las imágenes procesadas
    imagenes_procesadas = []

    for encoding in encodings:
        try:
            with open(ruta_archivo, "r", encoding=encoding) as f:
                contenido = f.read()

            soup = BeautifulSoup(contenido, "html.parser")

            # Extraer título
            titulo = None
            if soup.title:
                titulo = soup.title.string
            elif soup.find("h1"):
                titulo = soup.find("h1").get_text()

            # Procesar imágenes: descargarlas y actualizar rutas
            # Obtener la lista de imágenes procesadas
            imagenes_procesadas = procesar_imagenes(soup, dir_adjuntos, ruta_archivo)

            # Preprocesar elementos específicos de Word
            preprocesar_elementos_word(soup)

            # Convertir HTML a Markdown
            if PANDOC_AVAILABLE:
                # Usar Pandoc para una mejor conversión (especialmente tablas)
                markdown = html_a_markdown_pandoc(str(soup), dir_adjuntos)
            else:
                # Fallback a html2text si Pandoc no está disponible
                markdown = html_a_markdown_html2text(soup)

            # Post-procesar el Markdown para formato Obsidian
            markdown = post_procesar_markdown(markdown, imagenes_procesadas)

            # Crear la página HTML con la lista de imágenes
            return PaginaHTML(
                ruta_archivo=ruta_archivo, 
                titulo=titulo, 
                contenido=markdown,
                imagenes=imagenes_procesadas
            )

        except UnicodeDecodeError:
            # Si falla la codificación, probar con la siguiente
            continue
        except Exception as e:
            print(
                f"Error procesando archivo HTML {ruta_archivo} con encoding {encoding}: {str(e)}"
            )
            # Continuar con el siguiente encoding si es un error de codificación
            if isinstance(e, UnicodeError):
                continue
            # Para otros errores, devolver página con error
            return PaginaHTML(
                ruta_archivo=ruta_archivo, contenido=f"Error al procesar: {str(e)}"
            )

    # Si todos los encodings fallaron
    return PaginaHTML(
        ruta_archivo=ruta_archivo,
        contenido=f"Error al procesar: No se pudo decodificar el archivo con ninguna codificación compatible",
    )


def preprocesar_elementos_word(soup):
    """
    Preprocesa elementos específicos de Word para mejorar la conversión.
    """
    # Eliminar metadatos y scripts
    for tag in soup.select('style, script, meta, link'):
        tag.decompose()
    
    # Intentar limpiar elementos Office específicos
    for tag in soup.find_all(['o:p']):
        if tag.string:
            # Preservar solo el texto
            tag.replace_with(soup.new_string(tag.get_text()))
        else:
            tag.decompose()
    
    # Convertir elementos VML a elementos HTML estándar
    for shape in soup.find_all(['v:shape', 'v:imagedata']):
        # Extraer cualquier imagen dentro del shape
        img_tags = shape.find_all('img')
        if img_tags:
            for img in img_tags:
                # Mover la imagen fuera del shape
                if shape.parent:
                    shape.parent.insert_before(img)
        
        # Extraer cualquier texto significativo
        text_content = shape.get_text().strip()
        if text_content and shape.parent:
            text_node = soup.new_tag('p')
            text_node.string = text_content
            shape.parent.insert_before(text_node)


def html_a_markdown_pandoc(html_content, dir_adjuntos):
    """
    Convierte HTML a Markdown usando Pandoc.
    Pandoc maneja mucho mejor tablas, listas y otros elementos complejos.
    """
    try:
        # Guardar HTML a un archivo temporal
        temp_html_path = os.path.join(dir_adjuntos, "temp_conversion.html")
        with open(temp_html_path, "w", encoding="utf-8") as f:
            f.write(html_content)
        
        # Convertir usando pypandoc (interfaz para Pandoc)
        # Opciones de Pandoc para mejor manejo de tablas
        extra_args = [
            '--wrap=none',  # No wrap lines
            '--extract-media=' + dir_adjuntos,  # Extract media to the specified directory
            '--standalone'  # Process as a standalone document
        ]
        
        # Usar GitHub Flavored Markdown para mejor soporte de tablas
        markdown = pypandoc.convert_file(
            temp_html_path, 
            'gfm',  # GitHub Flavored Markdown
            extra_args=extra_args
        )
        
        # Limpiar archivo temporal
        if os.path.exists(temp_html_path):
            os.remove(temp_html_path)
            
        return markdown
    except Exception as e:
        print(f"Error en conversión con Pandoc: {str(e)}")
        # Fallback al método antiguo si Pandoc falla
        if os.path.exists(temp_html_path):
            with open(temp_html_path, "r", encoding="utf-8") as f:
                soup = BeautifulSoup(f.read(), "html.parser")
            os.remove(temp_html_path)
            return html_a_markdown_html2text(soup)
        else:
            return f"Error en conversión: {str(e)}"


def html_a_markdown_html2text(soup):
    """
    Método de fallback: convierte HTML a texto Markdown utilizando html2text.
    """
    # Configurar el conversor html2text
    h2t = html2text.HTML2Text()
    h2t.body_width = 0  # No limitar el ancho del cuerpo del texto
    h2t.ignore_links = False
    h2t.ignore_images = False
    h2t.ignore_emphasis = False
    h2t.ignore_tables = False
    h2t.bypass_tables = False
    h2t.mark_code = True
    h2t.unicode_snob = True
    
    # Procesar tablas para prepararlas para conversión
    for table in soup.find_all("table"):
        # Agregar clase para mejor reconocimiento
        table["class"] = table.get("class", []) + ["md-table"]
        
        # Verificar si la tabla tiene encabezados
        rows = table.find_all("tr")
        if rows and not table.find("th"):
            # Convertir primera fila a encabezados si no hay th
            first_row_cells = rows[0].find_all("td")
            for cell in first_row_cells:
                new_th = soup.new_tag("th")
                new_th.string = cell.get_text().strip()
                cell.replace_with(new_th)
    
    # Convertir a Markdown
    html_content = str(soup)
    markdown_content = h2t.handle(html_content)
    
    return markdown_content


def post_procesar_markdown(markdown, imagenes_procesadas):
    """
    Post-procesa el Markdown para adaptarlo a formato Obsidian y
    corregir problemas comunes de conversión.
    """
    # 1. Convertir referencias de imágenes al formato Obsidian
    for imagen in imagenes_procesadas:
        # Buscar referencias de imágenes en formato estándar
        # y convertirlas al formato Obsidian
        markdown = re.sub(
            r'!\[(.*?)\]\((.*?' + re.escape(imagen) + r')\)',
            f'![[{imagen}]]',
            markdown
        )
    
    # 2. Limpiar líneas excesivas
    markdown = re.sub(r'\n{3,}', '\n\n', markdown)
    
    # 3. Arreglar listas mal formadas
    markdown = re.sub(r'(^|\n)([*\-])([^\s])', r'\1\2 \3', markdown, flags=re.MULTILINE)
    
    # 4. Mejorar formato de tablas
    # Asegurar que hay línea en blanco antes y después de tablas
    markdown = re.sub(r'([^\n])\n(\|[^\n]+\|)', r'\1\n\n\2', markdown)
    markdown = re.sub(r'(\|[^\n]+\|)\n([^\n\|])', r'\1\n\n\2', markdown)
    
    # 5. Eliminar caracteres de control
    markdown = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', markdown)
    
    # 6. Normalizar líneas horizontales
    markdown = re.sub(r'(\*{3,}|\-{3,}|_{3,})', '---', markdown)
    
    return markdown


def procesar_imagenes(soup, dir_adjuntos, ruta_archivo_html):
    """
    Procesa todas las imágenes en el HTML, descargándolas al directorio
    de adjuntos y actualizando las rutas.
    
    Versión mejorada con manejo simplificado de rutas y mejor
    detección de imágenes en documentos Word.
    """
    # Crear directorio si no existe
    os.makedirs(dir_adjuntos, exist_ok=True)
    
    # Directorio base del archivo HTML
    dir_base = os.path.dirname(os.path.abspath(ruta_archivo_html))
    
    # Lista para almacenar imágenes procesadas
    imagenes_procesadas = []
    
    # Estadísticas
    stats = {
        "procesadas": 0,
        "no_encontradas": 0,
        "con_error": 0
    }
    
    # Función auxiliar para procesar una imagen
    def procesar_imagen(src, img_tag=None):
        if not src or src.startswith("data:"):
            return None
            
        try:
            print(f"Procesando imagen: {src}")
            
            # Determinar si es URL o ruta local
            if src.startswith(("http://", "https://")):
                # Es una URL remota
                nombre_archivo = os.path.basename(urlparse(src).path)
                nombre_archivo = unquote(nombre_archivo)
                
                # Limpiar nombre de archivo
                nombre_archivo = re.sub(r'[<>:"/\\|?*]', "_", nombre_archivo)
                if not nombre_archivo or nombre_archivo == "_":
                    nombre_archivo = f"image_{hashlib.md5(src.encode()).hexdigest()[:8]}.jpg"
                    
                # Descargar imagen
                ruta_img = os.path.join(dir_adjuntos, nombre_archivo)
                try:
                    response = requests.get(src, stream=True, timeout=10)
                    if response.status_code == 200:
                        with open(ruta_img, "wb") as f:
                            response.raw.decode_content = True
                            shutil.copyfileobj(response.raw, f)
                        
                        # Actualizar src en el tag HTML
                        if img_tag:
                            img_tag["src"] = nombre_archivo
                            
                        return nombre_archivo
                except Exception as e:
                    print(f"Error descargando imagen {src}: {str(e)}")
                    stats["con_error"] += 1
                    return None
            else:
                # Es una ruta local - intentar múltiples ubicaciones posibles
                src_decoded = unquote(src)
                posibles_rutas = [
                    os.path.join(dir_base, src_decoded),  # Ruta completa
                    os.path.join(dir_base, os.path.basename(src_decoded)),  # Solo el nombre
                    
                    # Rutas comunes para archivos Word
                    os.path.join(
                        dir_base, 
                        os.path.splitext(os.path.basename(ruta_archivo_html))[0] + "_archivos", 
                        os.path.basename(src_decoded)
                    ),
                    os.path.join(
                        dir_base, 
                        os.path.splitext(os.path.basename(ruta_archivo_html))[0] + "_files", 
                        os.path.basename(src_decoded)
                    ),
                    os.path.join(dir_base, "image", os.path.basename(src_decoded))
                ]
                
                # Probar cada ruta posible
                for ruta in posibles_rutas:
                    if os.path.exists(ruta):
                        # Copiar imagen a adjuntos
                        nombre_archivo = os.path.basename(ruta)
                        nombre_archivo = re.sub(r'[<>:"/\\|?*]', "_", nombre_archivo)
                        ruta_img_destino = os.path.join(dir_adjuntos, nombre_archivo)
                        
                        shutil.copy2(ruta, ruta_img_destino)
                        
                        # Actualizar src en el tag HTML
                        if img_tag:
                            img_tag["src"] = nombre_archivo
                            
                        return nombre_archivo
                
                print(f"Imagen no encontrada en ninguna ruta: {src}")
                stats["no_encontradas"] += 1
                return None
        except Exception as e:
            print(f"Error procesando imagen {src}: {str(e)}")
            stats["con_error"] += 1
            return None
    
    # 1. Procesar imágenes normales
    for img in soup.find_all("img"):
        src = img.get("src")
        nombre_archivo = procesar_imagen(src, img)
        if nombre_archivo and nombre_archivo not in imagenes_procesadas:
            imagenes_procesadas.append(nombre_archivo)
            stats["procesadas"] += 1
    
    # 2. Procesar imágenes VML (Office)
    for img_data in soup.find_all("v:imagedata"):
        src = img_data.get("src")
        nombre_archivo = procesar_imagen(src)
        if nombre_archivo and nombre_archivo not in imagenes_procesadas:
            imagenes_procesadas.append(nombre_archivo)
            stats["procesadas"] += 1
            
            # Crear un tag img estándar para asegurar que aparezca en Markdown
            new_img = soup.new_tag("img", src=nombre_archivo)
            parent = img_data.find_parent()
            if parent:
                parent.insert_after(new_img)
    
    # 3. Procesar referencias de imágenes en estilos
    style_tags = soup.find_all(["style", lambda tag: tag.has_attr("style")])
    for tag in style_tags:
        style_content = tag.get("style", "") if tag.name != "style" else tag.string or ""
        img_refs = re.findall(r'url\(["\']?([^"\']+)["\']?\)', style_content)
        for img_ref in img_refs:
            nombre_archivo = procesar_imagen(img_ref)
            if nombre_archivo and nombre_archivo not in imagenes_procesadas:
                imagenes_procesadas.append(nombre_archivo)
                stats["procesadas"] += 1
    
    # Mostrar resumen
    print(f"\nResumen de procesamiento de imágenes:")
    print(f"- Imágenes procesadas con éxito: {stats['procesadas']}")
    print(f"- Imágenes no encontradas: {stats['no_encontradas']}")
    print(f"- Imágenes con error de procesamiento: {stats['con_error']}")
    
    return imagenes_procesadas