import customtkinter as ctk
from core.process_runner import ProcessRunner
from core import reporting
import threading
from tkinter import filedialog
import os

class WifiFrame(ctk.CTkFrame):
    def __init__(self, master, set_session_callback=None, heading_font=None, text_font=None):
        super().__init__(master)
        self.set_session_callback = set_session_callback
        self.heading_font = heading_font if heading_font else ctk.CTkFont(size=18, weight="bold")
        self.text_font = text_font if text_font else ctk.CTkFont(size=12)
        self.monospace_font = ctk.CTkFont(family="Fira Code", size=12) # For output textboxes
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=1)

        title = ctk.CTkLabel(self, text="Sección de Hacking Wi-Fi", font=self.heading_font)
        title.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="ew")

        self.tab_view = ctk.CTkTabview(self)
        self.tab_view.grid(row=1, column=0, padx=20, pady=10, sticky="nsew")

        self.tab_view.add("Airmon-ng")
        self.tab_view.add("Airodump-ng")
        self.tab_view.add("Aireplay-ng")
        self.tab_view.add("Aircrack-ng")

        self.airmon_process_runner = None
        self.airodump_process_runner = None
        self.aireplay_process_runner = None
        self.aircrack_process_runner = None
        self.current_session_name = None

        self.setup_airmon_tab()
        self.setup_airodump_tab()
        self.setup_aireplay_tab()
        self.setup_aircrack_tab()

    def setup_airmon_tab(self):
        tab = self.tab_view.tab("Airmon-ng")
        tab.grid_columnconfigure(0, weight=1)
        tab.grid_rowconfigure(4, weight=1)

        # ---- Controles ----
        controls_frame = ctk.CTkFrame(tab)
        controls_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
        controls_frame.grid_columnconfigure(0, weight=1)
        controls_frame.grid_columnconfigure(1, weight=3)

        # Interface
        self.airmon_interface_label = ctk.CTkLabel(controls_frame, text="Interfaz de Red:")
        self.airmon_interface_label.grid(row=0, column=0, padx=10, pady=8, sticky="w")
        self.airmon_interface_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: wlan0")
        self.airmon_interface_entry.grid(row=0, column=1, padx=10, pady=8, sticky="ew")

        # Command Type
        self.airmon_command_label = ctk.CTkLabel(controls_frame, text="Acción:")
        self.airmon_command_label.grid(row=1, column=0, padx=10, pady=8, sticky="w")
        self.airmon_command_option = ctk.CTkOptionMenu(controls_frame, values=["start", "stop", "check"])
        self.airmon_command_option.set("start")
        self.airmon_command_option.grid(row=1, column=1, padx=10, pady=8, sticky="ew")

        # Start/Stop Buttons
        buttons_frame = ctk.CTkFrame(tab)
        buttons_frame.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
        buttons_frame.grid_columnconfigure(0, weight=1)
        buttons_frame.grid_columnconfigure(1, weight=1)

        self.airmon_start_button = ctk.CTkButton(buttons_frame, text="Ejecutar Airmon-ng", command=self.start_airmon)
        self.airmon_start_button.grid(row=0, column=0, padx=10, pady=12, sticky="ew")

        self.airmon_stop_button = ctk.CTkButton(buttons_frame, text="Detener", command=self.stop_airmon, fg_color="red", state="disabled")
        self.airmon_stop_button.grid(row=0, column=1, padx=10, pady=12, sticky="ew")
        
        # ---- Output ----
        self.airmon_output_textbox = ctk.CTkTextbox(tab, state="disabled", font=self.monospace_font)
        self.airmon_output_textbox.grid(row=4, column=0, padx=10, pady=10, sticky="nsew")

    def start_airmon(self):
        interface = self.airmon_interface_entry.get()
        command_type = self.airmon_command_option.get()

        if not interface:
            return

        self.airmon_start_button.configure(state="disabled")
        self.airmon_stop_button.configure(state="normal")
        
        self.current_session_name = reporting.create_session(f"airmon_{command_type}_{interface}")
        if self.set_session_callback:
            self.set_session_callback(self.current_session_name)
        
        command = ["sudo", "airmon-ng", command_type, interface]

        self.airmon_output_textbox.configure(state="normal")
        self.airmon_output_textbox.delete("1.0", "end")
        self.airmon_output_textbox.insert("1.0", f"--- Ejecutando Airmon-ng ---\nComando: {' '.join(command)}\n\n")
        self.airmon_output_textbox.configure(state="disabled")

        self.airmon_process_runner = ProcessRunner(command, self.airmon_output_textbox)
        self.airmon_process_runner.start()
        
        threading.Thread(target=self.wait_and_save_airmon, daemon=True).start()

    def stop_airmon(self):
        if self.airmon_process_runner:
            self.airmon_process_runner.stop()
            self.airmon_start_button.configure(state="normal")
            self.airmon_stop_button.configure(state="disabled")

    def wait_and_save_airmon(self):
        if self.airmon_process_runner:
            self.airmon_process_runner.thread.join()

        full_output = self.airmon_output_textbox.get("1.0", "end")
        reporting.log_tool_output(self.current_session_name, "Airmon-ng Output", full_output, output_format="txt")
        
        self.master.after(0, self._reset_airmon_buttons)

    def _reset_airmon_buttons(self):
        self.airmon_start_button.configure(state="normal")
        self.airmon_stop_button.configure(state="disabled")

    def setup_airodump_tab(self):
        tab = self.tab_view.tab("Airodump-ng")
        tab.grid_columnconfigure(0, weight=1)
        tab.grid_rowconfigure(4, weight=1)

        # ---- Controles ----
        controls_frame = ctk.CTkFrame(tab)
        controls_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
        controls_frame.grid_columnconfigure(0, weight=1)
        controls_frame.grid_columnconfigure(1, weight=3)

        # Interface
        self.airodump_interface_label = ctk.CTkLabel(controls_frame, text="Interfaz en Modo Monitor:")
        self.airodump_interface_label.grid(row=0, column=0, padx=10, pady=8, sticky="w")
        self.airodump_interface_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: wlan0mon")
        self.airodump_interface_entry.grid(row=0, column=1, padx=10, pady=8, sticky="ew")

        # Output Prefix
        self.airodump_output_prefix_label = ctk.CTkLabel(controls_frame, text="Prefijo de Archivo de Salida:")
        self.airodump_output_prefix_label.grid(row=1, column=0, padx=10, pady=8, sticky="w")
        self.airodump_output_prefix_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: captures")
        self.airodump_output_prefix_entry.grid(row=1, column=1, padx=10, pady=8, sticky="ew")

        # Start/Stop Buttons
        buttons_frame = ctk.CTkFrame(tab)
        buttons_frame.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
        buttons_frame.grid_columnconfigure(0, weight=1)
        buttons_frame.grid_columnconfigure(1, weight=1)

        self.airodump_start_button = ctk.CTkButton(buttons_frame, text="Iniciar Airodump-ng", command=self.start_airodump)
        self.airodump_start_button.grid(row=0, column=0, padx=10, pady=12, sticky="ew")

        self.airodump_stop_button = ctk.CTkButton(buttons_frame, text="Detener", command=self.stop_airodump, fg_color="red", state="disabled")
        self.airodump_stop_button.grid(row=0, column=1, padx=10, pady=12, sticky="ew")
        
        # ---- Output ----
        self.airodump_output_textbox = ctk.CTkTextbox(tab, state="disabled", font=self.monospace_font)
        self.airodump_output_textbox.grid(row=4, column=0, padx=10, pady=10, sticky="nsew")

    def start_airodump(self):
        interface = self.airodump_interface_entry.get()
        output_prefix = self.airodump_output_prefix_entry.get()

        if not interface:
            return

        self.airodump_start_button.configure(state="disabled")
        self.airodump_stop_button.configure(state="normal")
        
        self.current_session_name = reporting.create_session(f"airodump_{interface}")
        if self.set_session_callback:
            self.set_session_callback(self.current_session_name)
        
        command = ["sudo", "airodump-ng"]
        if output_prefix:
            command.extend(["-w", output_prefix])
        command.append(interface)

        self.airodump_output_textbox.configure(state="normal")
        self.airodump_output_textbox.delete("1.0", "end")
        self.airodump_output_textbox.insert("1.0", f"--- Ejecutando Airodump-ng ---\nComando: {' '.join(command)}\n\n")
        self.airodump_output_textbox.configure(state="disabled")

        self.airodump_process_runner = ProcessRunner(command, self.airodump_output_textbox)
        self.airodump_process_runner.start()
        
        threading.Thread(target=self.wait_and_save_airodump, daemon=True).start()

    def stop_airodump(self):
        if self.airodump_process_runner:
            self.airodump_process_runner.stop()
            self.airodump_start_button.configure(state="normal")
            self.airodump_stop_button.configure(state="disabled")

    def wait_and_save_airodump(self):
        if self.airodump_process_runner:
            self.airodump_process_runner.thread.join()

        full_output = self.airodump_output_textbox.get("1.0", "end")
        reporting.log_tool_output(self.current_session_name, "Airodump-ng Output", full_output, output_format="txt")
        
        self.master.after(0, self._reset_airodump_buttons)

    def _reset_airodump_buttons(self):
        self.airodump_start_button.configure(state="normal")
        self.airodump_stop_button.configure(state="disabled")

    def setup_aireplay_tab(self):
        tab = self.tab_view.tab("Aireplay-ng")
        tab.grid_columnconfigure(0, weight=1)
        tab.grid_rowconfigure(7, weight=1)

        # ---- Controles ----
        controls_frame = ctk.CTkFrame(tab)
        controls_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
        controls_frame.grid_columnconfigure(0, weight=1)
        controls_frame.grid_columnconfigure(1, weight=3)

        # Interface
        self.aireplay_interface_label = ctk.CTkLabel(controls_frame, text="Interfaz en Modo Monitor:")
        self.aireplay_interface_label.grid(row=0, column=0, padx=10, pady=8, sticky="w")
        self.aireplay_interface_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: wlan0mon")
        self.aireplay_interface_entry.grid(row=0, column=1, padx=10, pady=8, sticky="ew")

        # Attack Type
        self.aireplay_attack_label = ctk.CTkLabel(controls_frame, text="Tipo de Ataque:")
        self.aireplay_attack_label.grid(row=1, column=0, padx=5, pady=5, sticky="w")
        self.aireplay_attack_option = ctk.CTkOptionMenu(controls_frame, values=[
            "deauth (Ataque de Desautenticación)",
            "fakeauth (Autenticación Falsa)",
            "arp-replay (Replay de ARP)",
            "chopchop (Chopchop Attack)",
            "caffe-latte (Caffe-Latte Attack)"
        ])
        self.aireplay_attack_option.set("deauth (Ataque de Desautenticación)")
        self.aireplay_attack_option.grid(row=1, column=1, padx=5, pady=5, sticky="ew")

        # BSSID
        self.aireplay_bssid_label = ctk.CTkLabel(controls_frame, text="BSSID del AP:")
        self.aireplay_bssid_label.grid(row=2, column=0, padx=5, pady=5, sticky="w")
        self.aireplay_bssid_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: 00:11:22:33:44:55")
        self.aireplay_bssid_entry.grid(row=2, column=1, padx=5, pady=5, sticky="ew")

        # Client MAC (optional for deauth)
        self.aireplay_client_label = ctk.CTkLabel(controls_frame, text="MAC del Cliente (opcional):")
        self.aireplay_client_label.grid(row=3, column=0, padx=5, pady=5, sticky="w")
        self.aireplay_client_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: AA:BB:CC:DD:EE:FF o dejar en blanco para todos")
        self.aireplay_client_entry.grid(row=3, column=1, padx=5, pady=5, sticky="ew")

        # Packets (optional for deauth)
        self.aireplay_packets_label = ctk.CTkLabel(controls_frame, text="Número de Paquetes (opcional):")
        self.aireplay_packets_label.grid(row=4, column=0, padx=5, pady=5, sticky="w")
        self.aireplay_packets_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: 0 para continuo, o un número")
        self.aireplay_packets_entry.grid(row=4, column=1, padx=5, pady=5, sticky="ew")

        # Extra Arguments
        self.aireplay_extra_args_label = ctk.CTkLabel(controls_frame, text="Argumentos Extra Aireplay-ng:")
        self.aireplay_extra_args_label.grid(row=5, column=0, padx=5, pady=5, sticky="w")
        self.aireplay_extra_args_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: -h [MAC de origen]")
        self.aireplay_extra_args_entry.grid(row=5, column=1, padx=5, pady=5, sticky="ew")

        # Start/Stop Buttons
        buttons_frame = ctk.CTkFrame(tab)
        buttons_frame.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
        buttons_frame.grid_columnconfigure(0, weight=1)
        buttons_frame.grid_columnconfigure(1, weight=1)

        self.aireplay_start_button = ctk.CTkButton(buttons_frame, text="Iniciar Aireplay-ng", command=self.start_aireplay)
        self.aireplay_start_button.grid(row=0, column=0, padx=5, pady=10, sticky="ew")

        self.aireplay_stop_button = ctk.CTkButton(buttons_frame, text="Detener", command=self.stop_aireplay, fg_color="red", state="disabled")
        self.aireplay_stop_button.grid(row=0, column=1, padx=5, pady=10, sticky="ew")
        
        # ---- Output ----
        self.aireplay_output_textbox = ctk.CTkTextbox(tab, state="disabled", font=self.monospace_font)
        self.aireplay_output_textbox.grid(row=7, column=0, padx=10, pady=10, sticky="nsew")

    def start_aireplay(self):
        interface = self.aireplay_interface_entry.get()
        attack_type_raw = self.aireplay_attack_option.get()
        attack_type = attack_type_raw.split(" ")[0] # Get "deauth" from "deauth (Ataque...)"
        bssid = self.aireplay_bssid_entry.get()
        client_mac = self.aireplay_client_entry.get()
        packets = self.aireplay_packets_entry.get()
        extra_args = self.aireplay_extra_args_entry.get().split()

        if not interface or not bssid:
            return

        self.aireplay_start_button.configure(state="disabled")
        self.aireplay_stop_button.configure(state="normal")
        
        self.current_session_name = reporting.create_session(f"aireplay_{attack_type}_{bssid}")
        if self.set_session_callback:
            self.set_session_callback(self.current_session_name)
        
        command = ["sudo", "aireplay-ng", f"--{attack_type}"]
        
        if attack_type == "deauth":
            if packets:
                command.extend(["-a", bssid, "-c", client_mac, "-0", packets]) if client_mac else command.extend(["-a", bssid, "-0", packets])
            else:
                command.extend(["-a", bssid, "-c", client_mac, "-0", "0"]) if client_mac else command.extend(["-a", bssid, "-0", "0"])
        elif attack_type == "fakeauth":
            command.extend(["-a", bssid, "-h", client_mac, "-1", "0"]) if client_mac else command.extend(["-a", bssid, "-1", "0"])
        elif attack_type == "arp-replay":
            command.extend(["-b", bssid, "-3"])
        # Add logic for other attack types as needed
        
        command.extend(extra_args)
        command.append(interface)

        self.aireplay_output_textbox.configure(state="normal")
        self.aireplay_output_textbox.delete("1.0", "end")
        self.aireplay_output_textbox.insert("1.0", f"--- Ejecutando Aireplay-ng ---\nComando: {' '.join(command)}\n\n")
        self.aireplay_output_textbox.configure(state="disabled")

        self.aireplay_process_runner = ProcessRunner(command, self.aireplay_output_textbox)
        self.aireplay_process_runner.start()
        
        threading.Thread(target=self.wait_and_save_aireplay, daemon=True).start()

    def stop_aireplay(self):
        if self.aireplay_process_runner:
            self.aireplay_process_runner.stop()
            self.aireplay_start_button.configure(state="normal")
            self.aireplay_stop_button.configure(state="disabled")

    def wait_and_save_aireplay(self):
        if self.aireplay_process_runner:
            self.aireplay_process_runner.thread.join()

        full_output = self.aireplay_output_textbox.get("1.0", "end")
        reporting.log_tool_output(self.current_session_name, "Aireplay-ng Output", full_output, output_format="txt")
        
        self.master.after(0, self._reset_aireplay_buttons)

    def _reset_aireplay_buttons(self):
        self.aireplay_start_button.configure(state="normal")
        self.aireplay_stop_button.configure(state="disabled")

    def setup_aircrack_tab(self):
        tab = self.tab_view.tab("Aircrack-ng")
        tab.grid_columnconfigure(0, weight=1)
        tab.grid_rowconfigure(4, weight=1)

        # ---- Controles ----
        controls_frame = ctk.CTkFrame(tab)
        controls_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
        controls_frame.grid_columnconfigure(0, weight=1)
        controls_frame.grid_columnconfigure(1, weight=3)

        # Capture File
        cap_file_frame = ctk.CTkFrame(controls_frame, fg_color="transparent")
        cap_file_frame.grid(row=0, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
        cap_file_frame.grid_columnconfigure(0, weight=1)

        self.aircrack_cap_file_entry = ctk.CTkEntry(cap_file_frame, placeholder_text="Ruta al archivo .cap (ej: /path/to/capture.cap)")
        self.aircrack_cap_file_entry.grid(row=0, column=0, padx=(0, 5), sticky="ew")
        self.aircrack_cap_file_button = ctk.CTkButton(cap_file_frame, text="Seleccionar .cap", command=self.select_aircrack_cap_file, width=120)
        self.aircrack_cap_file_button.grid(row=0, column=1, padx=(5, 0))

        # Wordlist File
        wordlist_file_frame = ctk.CTkFrame(controls_frame, fg_color="transparent")
        wordlist_file_frame.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
        wordlist_file_frame.grid_columnconfigure(0, weight=1)

        self.aircrack_wordlist_file_entry = ctk.CTkEntry(wordlist_file_frame, placeholder_text="Ruta a la wordlist (ej: /usr/share/wordlists/rockyou.txt)")
        self.aircrack_wordlist_file_entry.grid(row=0, column=0, padx=(0, 5), sticky="ew")
        self.aircrack_wordlist_file_button = ctk.CTkButton(wordlist_file_frame, text="Seleccionar Wordlist", command=self.select_aircrack_wordlist_file, width=120)
        self.aircrack_wordlist_file_button.grid(row=0, column=1, padx=(5, 0))

        # BSSID (optional)
        self.aircrack_bssid_label = ctk.CTkLabel(controls_frame, text="BSSID del AP (opcional):")
        self.aircrack_bssid_label.grid(row=2, column=0, padx=5, pady=5, sticky="w")
        self.aircrack_bssid_entry = ctk.CTkEntry(controls_frame, placeholder_text="ej: 00:11:22:33:44:55 (para un AP específico)")
        self.aircrack_bssid_entry.grid(row=2, column=1, padx=5, pady=5, sticky="ew")

        # Start/Stop Buttons
        buttons_frame = ctk.CTkFrame(tab)
        buttons_frame.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
        buttons_frame.grid_columnconfigure(0, weight=1)
        buttons_frame.grid_columnconfigure(1, weight=1)

        self.aircrack_start_button = ctk.CTkButton(buttons_frame, text="Iniciar Aircrack-ng", command=self.start_aircrack)
        self.aircrack_start_button.grid(row=0, column=0, padx=5, pady=10, sticky="ew")

        self.aircrack_stop_button = ctk.CTkButton(buttons_frame, text="Detener", command=self.stop_aircrack, fg_color="red", state="disabled")
        self.aircrack_stop_button.grid(row=0, column=1, padx=5, pady=10, sticky="ew")
        
        # ---- Output ----
        self.aircrack_output_textbox = ctk.CTkTextbox(tab, state="disabled", font=self.monospace_font)
        self.aircrack_output_textbox.grid(row=4, column=0, padx=10, pady=10, sticky="nsew")

    def select_aircrack_cap_file(self):
        filepath = filedialog.askopenfilename(title="Seleccionar archivo .cap", filetypes=[("CAP files", "*.cap"), ("All files", "*.*")])
        if filepath:
            self.aircrack_cap_file_entry.delete(0, "end")
            self.aircrack_cap_file_entry.insert(0, filepath)

    def select_aircrack_wordlist_file(self):
        filepath = filedialog.askopenfilename(title="Seleccionar Wordlist")
        if filepath:
            self.aircrack_wordlist_file_entry.delete(0, "end")
            self.aircrack_wordlist_file_entry.insert(0, filepath)

    def start_aircrack(self):
        cap_file = self.aircrack_cap_file_entry.get()
        wordlist_file = self.aircrack_wordlist_file_entry.get()
        bssid = self.aircrack_bssid_entry.get()

        if not cap_file or not wordlist_file:
            return

        self.aircrack_start_button.configure(state="disabled")
        self.aircrack_stop_button.configure(state="normal")
        
        self.current_session_name = reporting.create_session(f"aircrack_{os.path.basename(cap_file)}")
        if self.set_session_callback:
            self.set_session_callback(self.current_session_name)
        
        command = ["sudo", "aircrack-ng"]
        if wordlist_file:
            command.extend(["-w", wordlist_file])
        if bssid:
            command.extend(["-b", bssid])
        command.append(cap_file)

        self.aircrack_output_textbox.configure(state="normal")
        self.aircrack_output_textbox.delete("1.0", "end")
        self.aircrack_output_textbox.insert("1.0", f"--- Ejecutando Aircrack-ng ---\nComando: {' '.join(command)}\n\n")
        self.aircrack_output_textbox.configure(state="disabled")

        self.aircrack_process_runner = ProcessRunner(command, self.aircrack_output_textbox)
        self.aircrack_process_runner.start()
        
        threading.Thread(target=self.wait_and_save_aircrack, daemon=True).start()

    def stop_aircrack(self):
        if self.aircrack_process_runner:
            self.aircrack_process_runner.stop()
            self.aircrack_start_button.configure(state="normal")
            self.aircrack_stop_button.configure(state="disabled")

    def wait_and_save_aircrack(self):
        if self.aircrack_process_runner:
            self.aircrack_process_runner.thread.join()

        full_output = self.aircrack_output_textbox.get("1.0", "end")
        reporting.log_tool_output(self.current_session_name, "Aircrack-ng Output", full_output, output_format="txt")
        
        self.master.after(0, self._reset_aircrack_buttons)

    def _reset_aircrack_buttons(self):
        self.aircrack_start_button.configure(state="normal")
        self.aircrack_stop_button.configure(state="disabled")