# -*- coding: utf-8 -*-
"""
Orquestación: procesar cada entrada del catálogo (SourceForge API o estándar).

- procesar_descarga_sourceforge: flujo para apps que usan solo la API de SF.
- procesar_descarga_estandar: flujo para enlace directo, scraping o GitHub.
- main: recorre DESCARGAS, elige procesador y escribe encabezado de log.
"""

import logging
import os
import re
import time
from urllib.parse import unquote, urljoin

from . import config
from . import download
from . import enlaces
from . import http_utils
from . import repo
from . import sourceforge
from . import utils


def procesar_descarga_sourceforge(descarga):
    """
    Gestiona una descarga alojada en SourceForge: API best_release, fallback
    por scraping si la extensión no coincide, luego descarga y actualización.
    """
    url = descarga["url"]
    nombre = descarga["nombre"]
    pattern = descarga["pattern"]
    extension = descarga["extension"]
    htmls = descarga["html"]
    m = re.search(r"sourceforge\.net/projects/([^/]+)", url)
    if not m:
        utils.registrar_error(f"{nombre}: No se pudo extraer el proyecto SourceForge de la URL: {url}")
        return False
    proyecto = m.group(1)
    sf_info = sourceforge.obtener_info_sourceforge(proyecto, nombre)
    if not sf_info:
        # Fallback: intentar scraping cuando la API no devuelve datos (ej. vuze)
        logging.warning(
            f"{nombre}: API de SourceForge no devolvió datos, intentando scraping como fallback..."
        )
        files_url = f"https://sourceforge.net/projects/{proyecto}/files/"
        headers = config.DEFAULT_HEADERS.copy()
        candidatos = sourceforge.buscar_mejor_enlace_sourceforge(
            files_url, extension, pattern, headers
        )
        mejor_version = None
        mejor_href = None
        for version, href in candidatos:
            if mejor_version is None or utils.comparar_versiones(version, mejor_version) > 0:
                mejor_version = version
                mejor_href = href
        if mejor_href:
            sf_url = urljoin(files_url, mejor_href)
            sf_filename = os.path.basename(sf_url.rstrip("/").replace("/download", ""))
            logging.info(f"{nombre}: Fallback scraping encontró: {sf_filename} -> {sf_url}")
        else:
            utils.registrar_error(f"{nombre}: No se pudo obtener información de SourceForge")
            return False
    else:
        sf_filename = sf_info["filename"]
        sf_url = sf_info["url"]
    if extension and not sf_filename.lower().endswith(extension.lower()):
        logging.warning(
            f"{nombre}: El archivo de SourceForge API ({sf_filename}) no tiene la extensión "
            f"esperada ({extension}). Intentando scraping como fallback..."
        )
        files_url = f"https://sourceforge.net/projects/{proyecto}/files/"
        headers = config.DEFAULT_HEADERS.copy()
        candidatos = sourceforge.buscar_mejor_enlace_sourceforge(
            files_url, extension, pattern, headers
        )
        if candidatos:
            mejor_version = None
            mejor_href = None
            for version, href in candidatos:
                if mejor_version is None or utils.comparar_versiones(version, mejor_version) > 0:
                    mejor_version = version
                    mejor_href = href
            if mejor_href:
                sf_url = urljoin(files_url, mejor_href)
                sf_filename = os.path.basename(
                    sf_url.rstrip("/").replace("/download", "")
                )
                logging.info(f"{nombre}: Fallback scraping encontró: {sf_filename} -> {sf_url}")
            else:
                utils.registrar_error(f"{nombre}: Scraping fallback tampoco encontró instalador válido")
                return False
        else:
            utils.registrar_error(
                f"{nombre}: No se encontró instalador válido en SourceForge para {proyecto}"
            )
            return False
    version_descargada = utils.obtener_version(pattern, sf_filename)
    if not version_descargada:
        utils.registrar_error(f"{nombre}: No se pudo determinar la versión desde {sf_filename}")
        return False
    version_descargada = utils.limpiar_version(version_descargada)
    logging.info(f"{nombre}: Versión detectada: {version_descargada}")
    archivo_a_reemplazar = repo.buscar_archivo_a_reemplazar(version_descargada, nombre)
    if not archivo_a_reemplazar:
        return True
    nombre_completo = f"{nombre.lower()}_{version_descargada}{extension}"
    ruta_destino = f"{config.REPO_DIR}{nombre_completo}"
    if download.descargar_con_wget(sf_url, ruta_destino, nombre):
        repo.modificar_htmls(htmls, nombre_completo, version_descargada, nombre)
        repo.gestionar_archivo_antiguo(archivo_a_reemplazar, nombre_completo, nombre)
        logging.info(f"{nombre}: OK: Proceso completado exitosamente")
        return True
    utils.registrar_error(f"{nombre}: ERROR: Descarga con wget falló")
    return False


def procesar_descarga_estandar(descarga):
    """
    Gestiona una descarga estándar: obtiene enlace (custom_fn, GitHub o scraping),
    resuelve URL si hace falta, comprueba versión, descarga y actualiza HTML/repo.
    """
    url = descarga["url"]
    nombre = descarga["nombre"]
    filtro = descarga["filtro"]
    pattern = descarga["pattern"]
    extension = descarga["extension"]
    htmls = descarga["html"]
    custom_fn = descarga.get("custom_fn")
    if custom_fn:
        enlace_descarga = custom_fn()
    elif descarga.get("github_repo"):
        enlace_descarga = enlaces.obtener_enlace_github_release(
            descarga["github_repo"], descarga["github_asset"], nombre
        )
    elif descarga.get("github") and nombre.lower() == "obsstudio":
        enlace_descarga = enlaces.obtener_enlace_obs_github()
    else:
        enlace_descarga = enlaces.obtener_enlace_descarga(
            url, filtro, pattern, extension, nombre
        )
    if not enlace_descarga:
        logging.warning(f"No se encontró el enlace de descarga para {nombre} en la página {url}")
        utils.registrar_error(f"{nombre}: No se encontró el enlace de descarga")
        return False
    logging.info(f"Enlace de descarga encontrado para {nombre}: {enlace_descarga}")
    usar_wget = (
        descarga.get("usar_wget", False)
        or descarga.get("github_repo")
        or descarga.get("github")
    )
    try:
        version_descargada = utils.obtener_version(pattern, unquote(enlace_descarga))
        url_real = enlace_descarga
        if not version_descargada:
            url_real_res, content_type, resp_headers = http_utils.resolver_url_descarga(
                enlace_descarga
            )
            if url_real_res != enlace_descarga:
                logging.info(f"URL resuelta para {nombre}: {url_real_res}")
                url_real = url_real_res
            if not usar_wget and content_type and "text/html" in content_type:
                utils.registrar_error(
                    f"Se recibió HTML en lugar de binario para {nombre}. URL: {url_real}"
                )
                return False
            version_descargada = utils.obtener_version(pattern, unquote(url_real))
            if not version_descargada and resp_headers:
                version_descargada = utils.obtener_version_header(pattern, resp_headers)
        if not version_descargada:
            utils.registrar_error(f"Descarga cancelada: No se pudo determinar la versión para {nombre}")
            return False
        version_descargada = utils.limpiar_version(version_descargada)
        logging.info(f"Versión detectada para {nombre}: {version_descargada}")
        archivo_a_reemplazar = repo.buscar_archivo_a_reemplazar(version_descargada, nombre)
        if not archivo_a_reemplazar:
            return True
        nombre_completo = f"{nombre.lower()}_{version_descargada}{extension}"
        ruta_destino = f"{config.REPO_DIR}{nombre_completo}"
        if usar_wget:
            exito = download.descargar_con_wget(enlace_descarga, ruta_destino, nombre)
        else:
            exito = download.descargar_con_requests(url_real, ruta_destino, nombre)
        if not exito and not usar_wget:
            logging.warning(f"{nombre}: Reintentando descarga con wget...")
            exito = download.descargar_con_wget(enlace_descarga, ruta_destino, nombre)
        if exito:
            repo.modificar_htmls(htmls, nombre_completo, version_descargada, nombre)
            repo.gestionar_archivo_antiguo(archivo_a_reemplazar, nombre_completo, nombre)
            logging.info(f"Descarga completada para {nombre}")
            return True
        return False
    except Exception as e:
        utils.registrar_error(f"Error al procesar {nombre}: {e}")
        return False


def main():
    """
    Punto de entrada: configura logging, escribe encabezado y procesa cada
    entrada del catálogo (SourceForge API o estándar). Los errores en una
    app no detienen el resto.
    """
    from . import catalogo

    config.configurar_logging()
    config.escribir_encabezado_ejecucion()
    for descarga in catalogo.DESCARGAS:
        try:
            nombre = descarga["nombre"]
            logging.info(f"Procesando {nombre}...")
            if (
                "sourceforge.net/projects/" in descarga["url"]
                and not descarga.get("custom_fn")
                and not descarga.get("github_repo")
            ):
                procesar_descarga_sourceforge(descarga)
            else:
                procesar_descarga_estandar(descarga)
        except Exception as e:
            utils.registrar_error(
                f"Error crítico procesando {descarga.get('nombre', 'desconocido')}: {e}"
            )
            continue
        finally:
            time.sleep(2)
    logging.info("Fin de la ejecución")
