import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
from pymongo import MongoClient
import time
import tkinter as tk
from tkinter import scrolledtext
import threading


def setup_database():
    """
    Configura la conexión a la base de datos MongoDB y retorna la colección.
    """
    client = MongoClient("mongodb://localhost:27017/")  # Conectar a MongoDB
    db = client["scraping"]  # Base de datos llamada 'scraping'
    collection = db["visited_links"]  # Colección llamada 'visited_links'

    # Crear índice único para evitar duplicados
    collection.create_index("url", unique=True)

    return collection


def insert_visited_link(collection, url):
    """
    Inserta una URL visitada en la base de datos.
    """
    try:
        collection.insert_one({"url": url})
    except Exception as e:
        print(f"Error al insertar la URL visitada {url}: {e}")


class ScraperApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Scraper de Enlaces con Hilos")
        self.root.geometry("800x500")

        # Base de datos MongoDB
        self.collection = setup_database()

        # Variable para detener el scraping
        self.running = False

        # Frame para la URL y botón
        frame_top = tk.Frame(self.root)
        frame_top.pack(pady=10)

        tk.Label(frame_top, text="Introduce la URL inicial:", font=("Arial", 12)).pack(side=tk.LEFT, padx=5)
        self.url_entry = tk.Entry(frame_top, width=50, font=("Arial", 12))
        self.url_entry.pack(side=tk.LEFT, padx=5)
        self.start_button = tk.Button(frame_top, text="Iniciar Scraping", font=("Arial", 12), command=self.start_scraping)
        self.start_button.pack(side=tk.LEFT, padx=5)

        self.stop_button = tk.Button(frame_top, text="Detener Scraping", font=("Arial", 12), command=self.stop_scraping, state="disabled")
        self.stop_button.pack(side=tk.LEFT, padx=5)

        # TextArea para los resultados
        self.result_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, font=("Arial", 12), height=20, width=90)
        self.result_area.pack(pady=10)

    def start_scraping(self):
        """
        Inicia el scraping en un hilo separado.
        """
        start_url = self.url_entry.get().strip()
        if not start_url:
            self.result_area.insert(tk.END, "Por favor, introduce una URL válida.\n")
            return

        self.result_area.insert(tk.END, f"Iniciando scraping desde: {start_url}\n")
        self.result_area.see(tk.END)

        self.running = True
        self.start_button.config(state="disabled")
        self.stop_button.config(state="normal")

        # Iniciar un hilo para el scraping
        self.scraping_thread = threading.Thread(target=self.scrape_links_forever, args=(start_url,))
        self.scraping_thread.daemon = True  # Hilo se detiene cuando la app se cierra
        self.scraping_thread.start()

    def stop_scraping(self):
        """
        Detiene el scraping.
        """
        self.running = False
        self.result_area.insert(tk.END, "Deteniendo scraping...\n")
        self.result_area.see(tk.END)
        self.start_button.config(state="normal")
        self.stop_button.config(state="disabled")

    def scrape_links_forever(self, start_url):
        """
        Scrapea enlaces indefinidamente y guarda solo los visitados en MongoDB.
        Se ejecuta en un hilo separado.
        
        Args:
            start_url (str): URL inicial para comenzar el scraping.
        """
        to_visit = [start_url]  # Lista de URLs por visitar
        visited = set()  # Conjunto para evitar bucles

        while to_visit and self.running:
            current_url = to_visit.pop(0)  # Tomar la URL actual de la lista

            if current_url in visited:
                continue

            self.result_area.insert(tk.END, f"Explorando: {current_url}\n")
            self.result_area.see(tk.END)
            visited.add(current_url)  # Marcar como visitada

            try:
                # Realizar la solicitud HTTP
                response = requests.get(current_url, timeout=10)
                response.raise_for_status()
            except requests.RequestException as e:
                self.result_area.insert(tk.END, f"Error al acceder a {current_url}: {e}\n")
                self.result_area.see(tk.END)
                continue

            # Parsear el contenido HTML
            soup = BeautifulSoup(response.text, 'html.parser')

            # Encontrar y procesar los enlaces
            for link in soup.find_all('a', href=True):
                full_url = urljoin(current_url, link['href'])
                if full_url not in visited and full_url not in to_visit:
                    to_visit.append(full_url)

            # Insertar la URL visitada en la base de datos
            insert_visited_link(self.collection, current_url)

            # Retardo para evitar sobrecargar el servidor
            time.sleep(1)

        # Finalización del scraping
        self.result_area.insert(tk.END, "Scraping finalizado.\n")
        self.result_area.see(tk.END)
        self.start_button.config(state="normal")
        self.stop_button.config(state="disabled")


if __name__ == "__main__":
    root = tk.Tk()
    app = ScraperApp(root)
    root.mainloop()
