# openai_api_key = "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"
# wordpress_url = "https://vrsimracers.com"
# wordpress_username = "elgatodeescayola"
# wordpress_password = "9cqIt77X14Juhbw4KStW68bu"

# PARTO DE 9,61€
# 1 NOTICIA PROCESADA: 9,53€ (PRECIO 8 CENTS.)
# 1 NOTICIA MÁS PROCESADA: 9,45€ (PRECIO 8 CENTS.)

import requests
from bs4 import BeautifulSoup
import openai
import base64
import time
from wordpress_xmlrpc import Client, WordPressPost
from wordpress_xmlrpc.methods import media, posts
from wordpress_xmlrpc.compat import xmlrpc_client
from io import BytesIO
import os
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import threading
import warnings
from urllib3.exceptions import InsecureRequestWarning
import threading
from tkinter import messagebox
import requests
from bs4 import BeautifulSoup
from bs4 import BeautifulSoup, NavigableString

# Configuración
news_sites = [
    "https://www.onlineracedriver.com/category/blog/",
    "https://traxion.gg/category/news/",
    "https://www.simracenews.com/sim-racing-news",
    "https://racesimcentral.net/itm/articles/",
    "https://boxthislap.org/",
    "https://www.bsimracing.com/",
    "https://www.overtake.gg/",
    "https://www.gtplanet.net/"
]
openai_api_key = "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"
wordpress_url = "https://iaranda.es/xmlrpc.php"  # Aseguramos usar HTTPS
wordpress_username = "admin"  # Reemplaza con tu usuario de WordPress
wordpress_password = "8pE0 NscB KDEB 8rfy jyF8 KQUu"  # Reemplaza con la contraseña de aplicación generada
blog_category_id = 47  # Reemplaza con el ID de categoría correcto
ARCHIVO_NOTICIAS_PROCESADAS = "noticias_procesadas.txt"
num_palabras = 1100

# Verificar HTTPS
if not wordpress_url.startswith('https://'):
    raise ValueError("La URL de WordPress debe usar HTTPS para Application Passwords")

# Configuración de autenticación básica
auth = (wordpress_username, wordpress_password)

# ... [Las funciones cargar_noticias_procesadas y guardar_noticias_procesadas permanecen igual] ...

# Suprimir advertencias de SSL inseguro
warnings.simplefilter('ignore', InsecureRequestWarning)


def redactar_noticia(noticia):
    try:
        prompt = f"""
        Título: {noticia['titulo']}
        
        Contenido original: {noticia['contenido'][:1000]}  # Limitamos a 1000 caracteres para evitar tokens excesivos
        
        Tarea: Reescribe y amplía esta noticia de simracing. Mantén la información esencial pero añade más contexto, 
        detalles y, si es apropiado, opiniones expertas. El tono debe ser informativo pero entretenido, 
        dirigido a entusiastas del simracing. La longitud debe ser de aproximadamente 500 palabras.
        """
        
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",  # Puedes cambiar a "gpt-4" si tienes acceso
            messages=[
                {"role": "system", "content": "Eres un periodista experto en simracing con un estilo de escritura atractivo y conocimientos profundos sobre juegos de carreras y simuladores."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=1000,
            n=1,
            stop=None,
            temperature=0.7,
        )
        return response.choices[0].message['content'].strip()
    except Exception as e:
        print(f"Error al redactar la noticia: {e}")
        return noticia['contenido']  # Devuelve el contenido original si hay un error
    

def parafrasear_titulo(titulo):
    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",  # Puedes cambiar a "gpt-4" si tienes acceso
            messages=[
                {"role": "system", "content": "Eres un asistente experto en parafrasear títulos de noticias de simracing. Tu tarea es reformular el título dado de manera atractiva y única, manteniendo su esencia y significado original."},
                {"role": "user", "content": f"Parafrasea el siguiente título de noticia de simracing: '{titulo}'"}
            ],
            max_tokens=60,
            n=1,
            stop=None,
            temperature=0.7,
        )
        return response.choices[0].message['content'].strip()
    except Exception as e:
        print(f"Error al parafrasear el título: {e}")
        return titulo  # Devuelve el título original si hay un error
    

def scrape_simracing_news(url):
    try:
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36"
        }
        response = requests.get(url, headers=headers, verify=False, timeout=10)
        response.raise_for_status()  # Esto levantará una excepción para códigos de estado HTTP no exitosos
        
        soup = BeautifulSoup(response.content, 'html5lib')
        
        articles = soup.find_all('article')
        if not articles:
            # Si no se encuentran artículos, buscar otros elementos comunes
            articles = soup.find_all(['div', 'section'], class_=['post', 'entry', 'article'])
        
        noticias_lista = []
        for article in articles:
            try:
                titular = article.find(['h1', 'h2', 'h3', 'a']).get_text(strip=True)
                link = article.find('a')
                if link and 'href' in link.attrs:
                    articulo_url = link['href']
                    if not articulo_url.startswith('http'):
                        # Handle relative URLs properly
                        if articulo_url.startswith('/'):
                            # Get the base domain
                            from urllib.parse import urlparse
                            parsed_uri = urlparse(url)
                            domain = '{uri.scheme}://{uri.netloc}'.format(uri=parsed_uri)
                            articulo_url = domain + articulo_url
                        else:
                            articulo_url = url.rstrip('/') + '/' + articulo_url.lstrip('/')
                    noticias_lista.append({"titulo": titular, "url": articulo_url})
            except Exception as e:
                print(f"Error al procesar un artículo en {url}: {e}")
                continue  # Continuar con el siguiente artículo
        
        print(f"Se encontraron {len(noticias_lista)} noticias en {url}")
        return noticias_lista
    except requests.RequestException as e:
        print(f"Error al hacer la solicitud a {url}: {e}")
    except Exception as e:
        print(f"Error inesperado al scrapear {url}: {e}")
    
    return []  # Devolver una lista vacía en caso de error

def obtener_contenido_completo(url):
    try:
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
        }
        response = requests.get(url, headers=headers, verify=False, timeout=10)
        response.raise_for_status()  # Esto lanzará una excepción para códigos de estado HTTP no exitosos
        
        soup = BeautifulSoup(response.content, 'html5lib')
        
        # Intenta encontrar el contenido principal del artículo
        content = None
        possible_content_classes = ['entry-content', 'post-content', 'article-content', 'content']
        for class_name in possible_content_classes:
            content = soup.find(['div', 'article'], class_=class_name)
            if content:
                break
        
        if not content:
            content = soup.find('article')
        
        if content:
            # Elimina elementos no deseados
            for element in content.find_all(['script', 'style', 'iframe', 'form']):
                element.decompose()
            
            # Obtiene el texto preservando la estructura
            paragraphs = []
            for p in content.find_all(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']):
                text = p.get_text(strip=True)
                if text:
                    paragraphs.append(text)
            
            return '\n\n'.join(paragraphs)
        else:
            return "No se pudo encontrar el contenido del artículo."
    
    except Exception as e:
        print(f"Error al obtener el contenido de {url}: {e}")
        return f"Error al obtener el contenido: {str(e)}"
    
    

class NoticiasScraper:
    def __init__(self, master):
        self.master = master
        self.master.title("Selector de Noticias de SimRacing")
        self.master.geometry("800x600")

        self.frame = ttk.Frame(self.master, padding="10")
        self.frame.pack(fill=tk.BOTH, expand=True)

        self.label = ttk.Label(self.frame, text="Selecciona las noticias que deseas procesar:")
        self.label.pack(pady=10)

        # Configurar el Treeview para permitir etiquetas personalizadas
        self.tree = ttk.Treeview(self.frame, columns=("Título", "Fuente"), show="headings")
        self.tree.heading("Título", text="Título")
        self.tree.heading("Fuente", text="Fuente")
        self.tree.tag_configure('procesada', foreground='red')
        self.tree.pack(fill=tk.BOTH, expand=True)

        self.scrollbar = ttk.Scrollbar(self.frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=self.scrollbar.set)

        self.progress = ttk.Progressbar(self.frame, orient=tk.HORIZONTAL, length=100, mode='determinate')
        self.progress.pack(fill=tk.X, pady=10)

        self.btn_frame = ttk.Frame(self.frame)
        self.btn_frame.pack(fill=tk.X, pady=10)

        self.btn_cargar = ttk.Button(self.btn_frame, text="Cargar Noticias", command=self.cargar_noticias)
        self.btn_cargar.pack(side=tk.LEFT, padx=5)

        self.btn_procesar = ttk.Button(self.btn_frame, text="Procesar Seleccionadas", command=self.procesar_seleccionadas)
        self.btn_procesar.pack(side=tk.RIGHT, padx=5)

        self.noticias = []
        self.noticias_procesadas = self.cargar_noticias_procesadas()


    def cargar_noticias_procesadas(self):
        noticias_procesadas = set()
        directorio_actual = os.path.dirname(os.path.abspath(__file__))
        ruta_archivo = os.path.join(directorio_actual, ARCHIVO_NOTICIAS_PROCESADAS)
        
        if os.path.exists(ruta_archivo):
            with open(ruta_archivo, "r") as archivo:
                for linea in archivo:
                    noticias_procesadas.add(linea.strip())
        
        return noticias_procesadas

    def guardar_noticias_procesadas(self):
        directorio_actual = os.path.dirname(os.path.abspath(__file__))
        ruta_archivo = os.path.join(directorio_actual, ARCHIVO_NOTICIAS_PROCESADAS)
        
        with open(ruta_archivo, "w") as archivo:
            for noticia in self.noticias_procesadas:
                archivo.write(noticia + "\n")

    def cargar_noticias(self):
        self.tree.delete(*self.tree.get_children())
        self.noticias = []
        self.progress['value'] = 0
        total_sites = len(news_sites)

        def cargar():
            for i, site in enumerate(news_sites):
                noticias_sitio = scrape_simracing_news(site)
                for noticia in noticias_sitio:
                    if noticia['titulo'].strip():  # Verifica si el título no está vacío
                        self.noticias.append(noticia)
                        # Comprueba si la noticia ya ha sido procesada
                        if noticia['titulo'] in self.noticias_procesadas:
                            self.tree.insert("", tk.END, values=(noticia['titulo'], site), tags=('procesada',))
                        else:
                            self.tree.insert("", tk.END, values=(noticia['titulo'], site))
                self.progress['value'] = (i + 1) / total_sites * 100
                self.master.update_idletasks()
            
            messagebox.showinfo("Carga Completa", f"Se han cargado {len(self.noticias)} noticias.")

        thread = threading.Thread(target=cargar)
        thread.start()

    def procesar_seleccionadas(self):
        seleccionadas = [self.noticias[self.tree.index(item)] for item in self.tree.selection()]
        if not seleccionadas:
            messagebox.showwarning("Advertencia", "No has seleccionado ninguna noticia.")
            return

        self.progress['value'] = 0
        total_noticias = len(seleccionadas)

        def procesar():
            for i, noticia in enumerate(seleccionadas):
                if noticia['titulo'] in self.noticias_procesadas:
                    print(f"La noticia '{noticia['titulo']}' ya ha sido procesada. Omitiendo...")
                
                try:
                    print(f"Procesando noticia: {noticia['titulo']}")
                    
                    # Obtener el contenido completo de la noticia
                    contenido_completo = obtener_contenido_completo(noticia['url'])
                    
                    # Parafrasear el título
                    titulo_parafraseado = parafrasear_titulo(noticia['titulo'])
                    print(f"Título parafraseado: {titulo_parafraseado}")
                    
                    # Redactar la noticia
                    contenido_redactado = redactar_noticia({'titulo': titulo_parafraseado, 'contenido': contenido_completo})
                    print("Contenido redactado completado.")
                    
                    # Generar imagen
                    imagen_data = generar_imagen({'titulo': titulo_parafraseado, 'contenido': contenido_redactado})
                    print("Imagen generada.")
                    
                    # Descargar la imagen
                    image_bytes = descargar_imagen(imagen_data, save_local=True)
                    print("Imagen descargada.")
                    
                    # Publicar en WordPress
                    publicar_noticia_wordpress(
                        {"titulo": titulo_parafraseado, "contenido": contenido_redactado}, 
                        contenido_redactado, 
                        image_bytes
                    )
                    print("Noticia publicada en WordPress.")
                    
                    self.noticias_procesadas.add(noticia['titulo'])
                    
                    time.sleep(5)
                    
                except Exception as e:
                    print(f"Error al procesar noticia {noticia['titulo']}: {e}")
                
                self.progress['value'] = (i + 1) / total_noticias * 100
                self.master.update_idletasks()

            self.guardar_noticias_procesadas()
            messagebox.showinfo("Proceso Completado", f"Se procesaron {len(seleccionadas)} noticias.")

        thread = threading.Thread(target=procesar)
        thread.start()

def generar_imagen(noticia):
    prompt = f"Una imagen en estilo 16-bit que represente la esencia de una noticia con título: {noticia['titulo']}"
    response = openai.Image.create(
        model="dall-e-3",
        prompt=prompt,
        n=1,
        size="1792x1024"
    )
    image_data = response['data'][0]['url']
    return image_data

def descargar_imagen(image_url, save_local=False):
    response = requests.get(image_url, verify=False)
    response.raise_for_status()
    if save_local:
        # Define un nombre de archivo basado en título y hora para evitar sobrescrituras
        filename = f"image_{int(time.time())}.jpeg"
        
        # Obtener el directorio actual del archivo Python
        current_directory = os.path.dirname(os.path.abspath(__file__))
        
        # Definir la ruta completa donde se guardará la imagen
        file_path = os.path.join(current_directory, filename)
        
        # Guardar la imagen en la ruta especificada
        with open(file_path, 'wb') as f:
            f.write(response.content)
        
        print(f"Imagen guardada localmente como {file_path}")
    return response.content

def publicar_noticia_wordpress(noticia, contenido, image_bytes):
    print(f"Publicando noticia en WordPress: {noticia['titulo']}")

    try:
        # Create WordPress client with proper error handling
        try:
            wp = Client(wordpress_url, wordpress_username, wordpress_password)
        except Exception as e:
            print(f"Error al conectar con WordPress: {e}")
            print(f"URL utilizada: {wordpress_url}")
            return False

        # Subir la imagen destacada a WordPress
        try:
            imagen_nombre = f"{noticia['titulo'][:50]}.jpg"  # Limit filename length
            imagen_data = BytesIO(image_bytes)
            data = {
                'name': imagen_nombre,
                'type': 'image/jpeg',
                'bits': xmlrpc_client.Binary(imagen_data.read())
            }
            response = wp.call(media.UploadFile(data))
            attachment_id = response['id']
        except Exception as e:
            print(f"Error al subir la imagen: {e}")
            attachment_id = None

        # Crear la entrada del blog
        post = WordPressPost()
        post.title = noticia['titulo']
        post.content = contenido
        post.post_status = 'publish'
        post.terms_names = {
            'category': ['Noticias']  # Asegúrate de que esta categoría existe
        }
        
        if attachment_id:
            post.thumbnail = attachment_id

        # Intentar publicar con reintentos
        max_retries = 3
        for attempt in range(max_retries):
            try:
                post_id = wp.call(posts.NewPost(post))
                print(f"Noticia publicada exitosamente con ID: {post_id}")
                return True
            except Exception as e:
                if attempt < max_retries - 1:
                    print(f"Intento {attempt + 1} fallido, reintentando...")
                    time.sleep(2)  # Esperar 2 segundos antes de reintentar
                else:
                    print(f"Error al publicar la noticia después de {max_retries} intentos: {e}")
                    return False

    except Exception as e:
        print(f"Error al procesar noticia {noticia['titulo']}: {e}")
        return False


def main():
    root = tk.Tk()
    app = NoticiasScraper(root)
    root.mainloop()

if __name__ == "__main__":
    main()