"""
Multi-AI Conversation GUI - Futuristic Edition
================================================
Interfață grafică futuristică pentru conversații multi-AI cu roluri specializate.
Design optimizat pentru Windows 11 cu culori neon/cyber și animații captivante.

Autor: Transformare din v4.py
Versiune: 1.0 Futuristic GUI Edition
"""

import customtkinter as ctk
import tkinter as tk
from tkinter import scrolledtext, messagebox
import requests
import json
import time
import os
import re
import sys
import threading
from typing import List, Dict, Any
from datetime import datetime

# ===== CONFIGURĂRI APLICAȚIE =====
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")

# Culori futuriste neon/cyber
COLORS = {
    'bg_dark': '#0a0e27',           # Background întunecat profund
    'bg_medium': '#1a1f3a',         # Background mediu
    'bg_light': '#2a2f4a',          # Background deschis
    'cyan': '#00FFFF',              # Cyan electric
    'magenta': '#FF00FF',           # Magenta neon
    'violet': '#8B00FF',            # Violet intens
    'electric_blue': '#7DF9FF',     # Albastru electric
    'neon_green': '#39FF14',        # Verde neon
    'neon_pink': '#FF10F0',         # Roz neon
    'text_primary': '#FFFFFF',      # Text alb
    'text_secondary': '#B8B8D1',    # Text secundar
    'success': '#00FF88',           # Verde succes
    'warning': '#FFD700',           # Galben warning
    'error': '#FF3366',             # Roșu eroare
}

PORT_DEFAULT = "11434"
MAX_RETRY = 3
LOG_FILE = os.path.join(os.path.dirname(__file__), "conversatie.txt")
CONFIG_FILE = os.path.join(os.path.dirname(__file__), "machines_config.json")
INTREBARE_IMPLICITA = "Ce idee concrete, ne-repetate, poți adăuga mai departe?"

# ===== FUNCȚII UTILITARE (din v4.py) =====

def validare_ip(ip_str: str) -> bool:
    """Validează format IP address."""
    pattern = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
    return re.match(pattern, ip_str) is not None

def write_to_log(message: str) -> None:
    """Scrie mesaj în fișierul log."""
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(message + "\n")

def norm_text(s: str) -> str:
    """Normalizează text pentru comparare."""
    s = s.lower()
    s = re.sub(r'\s+', ' ', s).strip()
    return s

def extrage_fraze_idei(text: str) -> List[str]:
    """
    Extrage fraze/idei din text pentru registru.
    Ignoră linii foarte scurte (<5 caractere).
    """
    chunks = re.split(r'[.\n•\-\u2022;]+', text)
    out = []
    for c in chunks:
        t = norm_text(c)
        if len(t) >= 5:
            out.append(t)
    return out

def sistem_role_prompt(subiect: str, rol: str, max_tokens: int) -> str:
    """Generează prompt sistem bazat pe rol."""
    baza = (
        f"Discuți despre: {subiect}. Răspunde concis (max {max_tokens} tokeni), clar, în română.\n"
        "Reguli generale:\n"
        "1) Evită repetițiile. 2) Adaugă cel puțin O IDEE NOUĂ față de lista 'Idei deja menționate'. "
        "3) Listează ideile ca bullets scurte, concrete, testabile. 4) Fii specific (cifre, condiții, costuri, pași)."
    )
    if rol == "generator":
        return baza + "\nRol: GENERATOR – propune idei noi, combinații neobișnuite, dar plauzibile."
    if rol == "critic":
        return baza + "\nRol: CRITIC – semnalează riscuri/limitări și oferă o îmbunătățire alternativă pentru fiecare punct."
    if rol == "moderator":
        return baza + "\nRol: MODERATOR – impune noutatea, rezumă scurt progresul, pune o întrebare-țintă pentru runda următoare."
    if rol in ("synth", "sintetizator", "synthesizer"):
        return baza + "\nRol: SINTETIZATOR – combină cele mai bune idei, elimină redundanța, propune o mini-foaie de parcurs."
    return baza + "\nRol: GENERAL – adaugă idei utile și noi."

def construieste_mesaje(conversatie: List[Dict[str, str]], subiect: str, rol: str, 
                       max_tokens: int, idei_deja: List[str]) -> List[Dict[str, str]]:
    """Construiește lista de mesaje pentru API chat."""
    sistem = sistem_role_prompt(subiect, rol, max_tokens)
    idei_block = ("Idei deja menționate până acum:\n- " + "\n- ".join(idei_deja[-30:]) 
                  if idei_deja else "Nu există idei înregistrate încă.")
    instructiune = (
        f"{idei_block}\n"
        "Asigură-te că fiecare punct nou este ne-repetat față de listă. "
        f"După idei, încheie cu: 'NEXT: {INTREBARE_IMPLICITA}'"
    )
    messages = [{"role": "system", "content": sistem}]
    messages.extend(conversatie)
    messages.append({"role": "user", "content": instructiune})
    return messages

def api_chat_stream(url: str, payload: Dict[str, Any], timeout: int, 
                   callback_token=None, callback_status=None) -> Dict[str, Any]:
    """
    Trimite cerere la API Ollama cu streaming.
    callback_token: funcție apelată pentru fiecare token primit
    callback_status: funcție apelată pentru actualizări status
    """
    retry = 0
    current_timeout = timeout
    raspuns_complet = ""
    tokens_eval = None
    
    while retry < MAX_RETRY:
        try:
            if callback_status:
                callback_status(f"🔄 Trimit cerere la {url} cu timeout {current_timeout}s...")
            
            resp = requests.post(f"http://{url}/api/chat", json=payload, 
                               timeout=current_timeout, stream=True)
            resp.raise_for_status()
            
            for line in resp.iter_lines():
                if not line:
                    continue
                chunk = json.loads(line)
                if "message" in chunk and "content" in chunk["message"]:
                    token = chunk["message"]["content"]
                    raspuns_complet += token
                    if callback_token:
                        callback_token(token)
                if chunk.get("done", False):
                    tokens_eval = chunk.get("eval_count", None)
                    break
            
            return {"text": raspuns_complet, "tokens": tokens_eval}
            
        except requests.exceptions.ReadTimeout:
            msg = f"⏱️ Timeout ({current_timeout}s) la {url}. Retry {retry+1}/{MAX_RETRY}..."
            if callback_status:
                callback_status(msg)
            write_to_log(msg)
            retry += 1
            current_timeout *= 2
            
        except requests.exceptions.RequestException as e:
            msg = f"❌ Eroare la {url}: {e}"
            if callback_status:
                callback_status(msg)
            write_to_log(msg)
            retry += 1
            current_timeout *= 2
    
    msg = f"❌ Maxim retry atins ({MAX_RETRY}) pentru {url}. Abort."
    if callback_status:
        callback_status(msg)
    write_to_log(msg)
    return {"text": None, "tokens": None}

# ===== FUNCȚII PERSISTENȚĂ CONFIGURAȚII =====

def save_machines_to_json(machines: List[Dict[str, Any]], filepath: str = CONFIG_FILE) -> bool:
    """
    Salvează configurațiile mașinilor în fișier JSON.
    Returns: True dacă salvarea a reușit, False altfel
    """
    try:
        # Creăm o copie fără referințele widget/tk
        machines_clean = []
        for m in machines:
            machine_data = {
                'ip': m['ip'],
                'port': m['port'],
                'model': m['model'],
                'rol': m['rol'],
                'max_tokens': m['max_tokens'],
                'timeout': m['timeout']
            }
            machines_clean.append(machine_data)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(machines_clean, f, indent=2, ensure_ascii=False)
        
        write_to_log(f"✅ Configurații salvate în {filepath} - {len(machines_clean)} mașini")
        return True
    
    except Exception as e:
        write_to_log(f"❌ Eroare la salvare configurații: {e}")
        return False

def load_machines_from_json(filepath: str = CONFIG_FILE) -> List[Dict[str, Any]]:
    """
    Încarcă configurațiile mașinilor din fișier JSON.
    Returns: Listă de configurații mașini sau listă goală dacă nu există/eroare
    """
    try:
        if not os.path.exists(filepath):
            write_to_log(f"ℹ️ Fișier configurație {filepath} nu există - se va folosi configurația default")
            return []
        
        with open(filepath, 'r', encoding='utf-8') as f:
            machines = json.load(f)
        
        # Validare configurații
        if not isinstance(machines, list):
            write_to_log(f"⚠️ Format invalid în {filepath} - se așteaptă listă de mașini")
            return []
        
        # Validare fiecare mașină
        valid_machines = []
        for idx, m in enumerate(machines):
            if not isinstance(m, dict):
                write_to_log(f"⚠️ Mașina #{idx+1} are format invalid - ignorată")
                continue
            
            required_fields = ['ip', 'port', 'model', 'rol', 'max_tokens', 'timeout']
            if not all(field in m for field in required_fields):
                write_to_log(f"⚠️ Mașina #{idx+1} are câmpuri lipsă - ignorată")
                continue
            
            if not validare_ip(m['ip']):
                write_to_log(f"⚠️ Mașina #{idx+1} are IP invalid: {m['ip']} - ignorată")
                continue
            
            valid_machines.append(m)
        
        write_to_log(f"✅ Încărcate {len(valid_machines)} mașini din {filepath}")
        return valid_machines
    
    except json.JSONDecodeError as e:
        write_to_log(f"❌ Eroare parsare JSON din {filepath}: {e}")
        return []
    except Exception as e:
        write_to_log(f"❌ Eroare la încărcare configurații: {e}")
        return []

def get_default_machines() -> List[Dict[str, Any]]:
    """
    Returnează configurațiile default pentru cele 2 mașini.
    """
    return [
        {
            'ip': '100.76.154.75',
            'port': PORT_DEFAULT,
            'model': 'qwen3-coder:480b-cloud',
            'rol': 'generator',
            'max_tokens': 700,
            'timeout': 120
        },
        {
            'ip': '100.73.213.29',
            'port': PORT_DEFAULT,
            'model': 'gpt-oss:120b-cloud',
            'rol': 'critic',
            'max_tokens': 700,
            'timeout': 120
        }
    ]

# ===== CLASĂ PRINCIPALĂ GUI =====

class MultiAIFuturisticGUI(ctk.CTk):
    def __init__(self):
        super().__init__()
        
        # Configurare fereastră
        self.title("🚀 Multi-AI Conversation · Futuristic Edition")
        self.geometry("1400x900")
        self.configure(fg_color=COLORS['bg_dark'])
        
        # State
        self.is_running = False
        self.stop_requested = False
        self.masini_config = []
        self.conversatie = []
        self.registry = {}
        
        # Protocol pentru închidere
        self.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # Build UI
        self.build_ui()
        self.setup_default_machines()
        
        # Inițializare log
        with open(LOG_FILE, "w", encoding="utf-8") as f:
            f.write(f"== Conversație Multi-AI · {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ==\n")
    
    def build_ui(self):
        """Construiește interfața grafică."""
        
        # === HEADER ===
        header = ctk.CTkFrame(self, fg_color=COLORS['bg_medium'], height=80)
        header.pack(fill='x', padx=10, pady=(10, 5))
        header.pack_propagate(False)
        
        title_label = ctk.CTkLabel(
            header,
            text="🤖 MULTI-AI CONVERSATION SYSTEM",
            font=("Orbitron", 32, "bold"),
            text_color=COLORS['cyan']
        )
        title_label.pack(pady=20)
        
        # === MAIN CONTAINER ===
        main_container = ctk.CTkFrame(self, fg_color='transparent')
        main_container.pack(fill='both', expand=True, padx=10, pady=5)
        
        # Left Panel - Configuration
        left_panel = ctk.CTkFrame(main_container, fg_color=COLORS['bg_medium'], width=500)
        left_panel.pack(side='left', fill='both', padx=(0, 5), pady=0)
        left_panel.pack_propagate(False)
        
        # Right Panel - Output & Status
        right_panel = ctk.CTkFrame(main_container, fg_color=COLORS['bg_medium'])
        right_panel.pack(side='right', fill='both', expand=True, padx=(5, 0), pady=0)
        
        # === LEFT PANEL CONTENT ===
        self.build_left_panel(left_panel)
        
        # === RIGHT PANEL CONTENT ===
        self.build_right_panel(right_panel)
    
    def build_left_panel(self, parent):
        """Construiește panoul stâng cu configurări."""
        
        # Tabview pentru organizare
        tabview = ctk.CTkTabview(parent, fg_color=COLORS['bg_light'], 
                                segmented_button_fg_color=COLORS['bg_dark'],
                                segmented_button_selected_color=COLORS['violet'],
                                segmented_button_selected_hover_color=COLORS['magenta'])
        tabview.pack(fill='both', expand=True, padx=10, pady=10)
        
        # Taburi
        tab_general = tabview.add("⚙️ General")
        tab_masini = tabview.add("🤖 Mașini")
        
        # === TAB GENERAL ===
        self.build_general_tab(tab_general)
        
        # === TAB MASINI ===
        self.build_masini_tab(tab_masini)
    
    def build_general_tab(self, parent):
        """Construiește tab-ul cu setări generale."""
        
        # Scrollable frame
        scroll = ctk.CTkScrollableFrame(parent, fg_color='transparent')
        scroll.pack(fill='both', expand=True, padx=5, pady=5)
        
        # Subiect discuție
        ctk.CTkLabel(scroll, text="📝 Tema de discuție", 
                    font=("Consolas", 14, "bold"), text_color=COLORS['electric_blue']).pack(anchor='w', pady=(10, 5))
        self.entry_subiect = ctk.CTkEntry(
            scroll, 
            placeholder_text="Introdu tema conversației...",
            height=40,
            font=("Consolas", 12),
            fg_color=COLORS['bg_dark'],
            border_color=COLORS['cyan'],
            border_width=2
        )
        self.entry_subiect.pack(fill='x', pady=(0, 15))
        
        # Număr runde
        ctk.CTkLabel(scroll, text="🔄 Număr de runde (0 = infinit)", 
                    font=("Consolas", 14, "bold"), text_color=COLORS['electric_blue']).pack(anchor='w', pady=(10, 5))
        self.entry_runde = ctk.CTkEntry(
            scroll,
            placeholder_text="5",
            height=40,
            font=("Consolas", 12),
            fg_color=COLORS['bg_dark'],
            border_color=COLORS['cyan'],
            border_width=2
        )
        self.entry_runde.insert(0, "5")
        self.entry_runde.pack(fill='x', pady=(0, 15))
        
        # Pauză între runde
        ctk.CTkLabel(scroll, text="⏱️ Pauză între runde (secunde)", 
                    font=("Consolas", 14, "bold"), text_color=COLORS['electric_blue']).pack(anchor='w', pady=(10, 5))
        self.entry_pauza = ctk.CTkEntry(
            scroll,
            placeholder_text="3",
            height=40,
            font=("Consolas", 12),
            fg_color=COLORS['bg_dark'],
            border_color=COLORS['cyan'],
            border_width=2
        )
        self.entry_pauza.insert(0, "3")
        self.entry_pauza.pack(fill='x', pady=(0, 15))
        
        # Separator
        separator = ctk.CTkFrame(scroll, height=2, fg_color=COLORS['violet'])
        separator.pack(fill='x', pady=20)
        
        # Info box
        info_frame = ctk.CTkFrame(scroll, fg_color=COLORS['bg_dark'], border_width=2, border_color=COLORS['neon_green'])
        info_frame.pack(fill='x', pady=10)
        
        info_text = (
            "ℹ️ INFORMAȚII:\n\n"
            "• Configurează tema și setările generale aici\n"
            "• Mergi la tab 🤖 Mașini pentru a configura agenții AI\n"
            "• Fiecare mașină poate avea rol diferit:\n"
            "  - Generator: idei noi\n"
            "  - Critic: analiză și îmbunătățiri\n"
            "  - Moderator: sinteză și direcție\n"
            "  - Sintetizator: combină și organizează\n"
            "• Conversația va fi afișată în panoul din dreapta"
        )
        
        ctk.CTkLabel(
            info_frame, 
            text=info_text,
            font=("Consolas", 10),
            text_color=COLORS['text_secondary'],
            justify='left'
        ).pack(padx=15, pady=15, anchor='w')
    
    def build_masini_tab(self, parent):
        """Construiește tab-ul cu configurare mașini."""
        
        # Top buttons - Row 1
        btn_frame_1 = ctk.CTkFrame(parent, fg_color='transparent')
        btn_frame_1.pack(fill='x', padx=5, pady=(5, 5))
        
        self.btn_add_machine = ctk.CTkButton(
            btn_frame_1,
            text="➕ Adaugă Mașină",
            command=self.add_machine_dialog,
            font=("Consolas", 11, "bold"),
            fg_color=COLORS['neon_green'],
            hover_color=COLORS['success'],
            text_color=COLORS['bg_dark'],
            height=35
        )
        self.btn_add_machine.pack(side='left', padx=3)
        
        self.btn_remove_machine = ctk.CTkButton(
            btn_frame_1,
            text="➖ Șterge Selectată",
            command=self.remove_selected_machine,
            font=("Consolas", 11, "bold"),
            fg_color=COLORS['error'],
            hover_color='#CC0033',
            text_color=COLORS['text_primary'],
            height=35
        )
        self.btn_remove_machine.pack(side='left', padx=3)
        
        # Top buttons - Row 2 (Persistență)
        btn_frame_2 = ctk.CTkFrame(parent, fg_color='transparent')
        btn_frame_2.pack(fill='x', padx=5, pady=(0, 10))
        
        self.btn_save_config = ctk.CTkButton(
            btn_frame_2,
            text="💾 Salvează Config",
            command=self.save_config_manual,
            font=("Consolas", 11, "bold"),
            fg_color=COLORS['violet'],
            hover_color=COLORS['magenta'],
            text_color=COLORS['text_primary'],
            height=35
        )
        self.btn_save_config.pack(side='left', padx=3)
        
        self.btn_load_config = ctk.CTkButton(
            btn_frame_2,
            text="📂 Încarcă Config",
            command=self.load_config_manual,
            font=("Consolas", 11, "bold"),
            fg_color=COLORS['electric_blue'],
            hover_color=COLORS['cyan'],
            text_color=COLORS['bg_dark'],
            height=35
        )
        self.btn_load_config.pack(side='left', padx=3)
        
        self.btn_reset_default = ctk.CTkButton(
            btn_frame_2,
            text="🔄 Reset la Default",
            command=self.reset_to_default,
            font=("Consolas", 11, "bold"),
            fg_color=COLORS['warning'],
            hover_color='#FFB700',
            text_color=COLORS['bg_dark'],
            height=35
        )
        self.btn_reset_default.pack(side='left', padx=3)
        
        # List of machines (scrollable)
        self.machines_frame = ctk.CTkScrollableFrame(parent, fg_color=COLORS['bg_dark'])
        self.machines_frame.pack(fill='both', expand=True, padx=5, pady=5)
        
        # Info label
        self.machines_info_label = ctk.CTkLabel(
            parent,
            text="📊 Total mașini: 0",
            font=("Consolas", 12, "bold"),
            text_color=COLORS['cyan']
        )
        self.machines_info_label.pack(pady=10)
    
    def build_right_panel(self, parent):
        """Construiește panoul drept cu output și status."""
        
        # Title
        title = ctk.CTkLabel(
            parent,
            text="💬 CONVERSAȚIE LIVE",
            font=("Orbitron", 20, "bold"),
            text_color=COLORS['magenta']
        )
        title.pack(pady=15)
        
        # Output area (custom text widget pentru culori)
        output_frame = ctk.CTkFrame(parent, fg_color=COLORS['bg_dark'])
        output_frame.pack(fill='both', expand=True, padx=10, pady=(0, 10))
        
        self.output_text = tk.Text(
            output_frame,
            wrap='word',
            font=("Consolas", 10),
            bg=COLORS['bg_dark'],
            fg=COLORS['text_primary'],
            insertbackground=COLORS['cyan'],
            selectbackground=COLORS['violet'],
            selectforeground=COLORS['text_primary'],
            relief='flat',
            padx=10,
            pady=10
        )
        self.output_text.pack(side='left', fill='both', expand=True)
        
        # Scrollbar
        scrollbar = ctk.CTkScrollbar(output_frame, command=self.output_text.yview)
        scrollbar.pack(side='right', fill='y')
        self.output_text.configure(yscrollcommand=scrollbar.set)
        
        # Configurare tag-uri pentru culori
        self.output_text.tag_config('header', foreground=COLORS['cyan'], font=("Consolas", 11, "bold"))
        self.output_text.tag_config('machine', foreground=COLORS['electric_blue'], font=("Consolas", 10, "bold"))
        self.output_text.tag_config('content', foreground=COLORS['text_primary'])
        self.output_text.tag_config('info', foreground=COLORS['neon_green'])
        self.output_text.tag_config('warning', foreground=COLORS['warning'])
        self.output_text.tag_config('error', foreground=COLORS['error'])
        self.output_text.tag_config('success', foreground=COLORS['success'])
        
        # Status bar
        status_frame = ctk.CTkFrame(parent, fg_color=COLORS['bg_light'], height=100)
        status_frame.pack(fill='x', padx=10, pady=(0, 10))
        status_frame.pack_propagate(False)
        
        self.status_label = ctk.CTkLabel(
            status_frame,
            text="⚡ Status: Inactiv · Gata de pornire",
            font=("Consolas", 11),
            text_color=COLORS['text_secondary']
        )
        self.status_label.pack(pady=5)
        
        # Progress bar
        self.progress = ctk.CTkProgressBar(
            status_frame,
            width=400,
            height=15,
            progress_color=COLORS['violet'],
            fg_color=COLORS['bg_dark']
        )
        self.progress.pack(pady=5)
        self.progress.set(0)
        
        # Control buttons
        btn_control_frame = ctk.CTkFrame(status_frame, fg_color='transparent')
        btn_control_frame.pack(pady=10)
        
        self.btn_start = ctk.CTkButton(
            btn_control_frame,
            text="🚀 START CONVERSAȚIE",
            command=self.start_conversation,
            font=("Orbitron", 16, "bold"),
            fg_color=COLORS['neon_green'],
            hover_color=COLORS['success'],
            text_color=COLORS['bg_dark'],
            height=50,
            width=250
        )
        self.btn_start.pack(side='left', padx=10)
        
        self.btn_stop = ctk.CTkButton(
            btn_control_frame,
            text="⏹️ STOP",
            command=self.stop_conversation,
            font=("Orbitron", 16, "bold"),
            fg_color=COLORS['error'],
            hover_color='#CC0033',
            text_color=COLORS['text_primary'],
            height=50,
            width=150,
            state='disabled'
        )
        self.btn_stop.pack(side='left', padx=10)
        
        self.btn_clear = ctk.CTkButton(
            btn_control_frame,
            text="🗑️ CLEAR",
            command=self.clear_output,
            font=("Consolas", 12, "bold"),
            fg_color=COLORS['bg_light'],
            hover_color=COLORS['bg_dark'],
            text_color=COLORS['text_secondary'],
            height=50,
            width=100
        )
        self.btn_clear.pack(side='left', padx=10)
    
    def setup_default_machines(self):
        """Încarcă configurații din JSON sau configurează cele 2 mașini default."""
        # Încearcă să încarce din JSON
        loaded_machines = load_machines_from_json()
        
        if loaded_machines:
            # Folosește configurațiile încărcate
            for machine in loaded_machines:
                self.add_machine_to_list(machine)
            
            # Afișează mesaj informativ
            msg = f"✅ Configurații încărcate: {len(loaded_machines)} mașini din {CONFIG_FILE}"
            messagebox.showinfo("Configurații Încărcate", msg)
            write_to_log(msg)
        else:
            # Folosește configurațiile default
            default_machines = get_default_machines()
            for machine in default_machines:
                self.add_machine_to_list(machine)
            
            msg = f"ℹ️ Folosesc configurație default: {len(default_machines)} mașini"
            write_to_log(msg)
            # Auto-salvează configurațiile default pentru viitoare porniri
            self.save_current_config()
    
    def add_machine_dialog(self):
        """Dialog pentru adăugare mașină nouă."""
        dialog = ctk.CTkToplevel(self)
        dialog.title("➕ Adaugă Mașină AI")
        dialog.geometry("500x650")
        dialog.configure(fg_color=COLORS['bg_dark'])
        dialog.resizable(False, False)
        
        # Make modal
        dialog.transient(self)
        dialog.grab_set()
        
        # Title
        ctk.CTkLabel(
            dialog,
            text="🤖 CONFIGURARE MAȘINĂ NOUĂ",
            font=("Orbitron", 18, "bold"),
            text_color=COLORS['cyan']
        ).pack(pady=20)
        
        # Form frame
        form = ctk.CTkFrame(dialog, fg_color=COLORS['bg_medium'])
        form.pack(fill='both', expand=True, padx=20, pady=(0, 20))
        
        # IP
        ctk.CTkLabel(form, text="🌐 IP Address:", font=("Consolas", 12, "bold"), 
                    text_color=COLORS['electric_blue']).pack(anchor='w', padx=20, pady=(20, 5))
        entry_ip = ctk.CTkEntry(form, placeholder_text="Ex: 100.76.154.75", height=35, 
                               font=("Consolas", 11), fg_color=COLORS['bg_dark'],
                               border_color=COLORS['cyan'], border_width=2)
        entry_ip.pack(fill='x', padx=20, pady=(0, 10))
        
        # Port
        ctk.CTkLabel(form, text="🔌 Port:", font=("Consolas", 12, "bold"), 
                    text_color=COLORS['electric_blue']).pack(anchor='w', padx=20, pady=(10, 5))
        entry_port = ctk.CTkEntry(form, placeholder_text=PORT_DEFAULT, height=35, 
                                 font=("Consolas", 11), fg_color=COLORS['bg_dark'],
                                 border_color=COLORS['cyan'], border_width=2)
        entry_port.insert(0, PORT_DEFAULT)
        entry_port.pack(fill='x', padx=20, pady=(0, 10))
        
        # Model
        ctk.CTkLabel(form, text="🧠 Model AI:", font=("Consolas", 12, "bold"), 
                    text_color=COLORS['electric_blue']).pack(anchor='w', padx=20, pady=(10, 5))
        entry_model = ctk.CTkEntry(form, placeholder_text="Ex: llama3:latest", height=35, 
                                  font=("Consolas", 11), fg_color=COLORS['bg_dark'],
                                  border_color=COLORS['cyan'], border_width=2)
        entry_model.pack(fill='x', padx=20, pady=(0, 10))
        
        # Rol
        ctk.CTkLabel(form, text="👤 Rol:", font=("Consolas", 12, "bold"), 
                    text_color=COLORS['electric_blue']).pack(anchor='w', padx=20, pady=(10, 5))
        entry_rol = ctk.CTkOptionMenu(
            form,
            values=["generator", "critic", "moderator", "synth"],
            height=35,
            font=("Consolas", 11),
            fg_color=COLORS['bg_dark'],
            button_color=COLORS['violet'],
            button_hover_color=COLORS['magenta']
        )
        entry_rol.set("generator")
        entry_rol.pack(fill='x', padx=20, pady=(0, 10))
        
        # Max tokens
        ctk.CTkLabel(form, text="📊 Max Tokens:", font=("Consolas", 12, "bold"), 
                    text_color=COLORS['electric_blue']).pack(anchor='w', padx=20, pady=(10, 5))
        entry_tokens = ctk.CTkEntry(form, placeholder_text="700", height=35, 
                                   font=("Consolas", 11), fg_color=COLORS['bg_dark'],
                                   border_color=COLORS['cyan'], border_width=2)
        entry_tokens.insert(0, "700")
        entry_tokens.pack(fill='x', padx=20, pady=(0, 10))
        
        # Timeout
        ctk.CTkLabel(form, text="⏱️ Timeout (sec):", font=("Consolas", 12, "bold"), 
                    text_color=COLORS['electric_blue']).pack(anchor='w', padx=20, pady=(10, 5))
        entry_timeout = ctk.CTkEntry(form, placeholder_text="120", height=35, 
                                    font=("Consolas", 11), fg_color=COLORS['bg_dark'],
                                    border_color=COLORS['cyan'], border_width=2)
        entry_timeout.insert(0, "120")
        entry_timeout.pack(fill='x', padx=20, pady=(0, 20))
        
        # Buttons
        btn_frame = ctk.CTkFrame(dialog, fg_color='transparent')
        btn_frame.pack(pady=10)
        
        def save_machine():
            ip = entry_ip.get().strip()
            if not validare_ip(ip):
                messagebox.showerror("Eroare", "❌ IP invalid! Format corect: 192.168.1.1")
                return
            
            port = entry_port.get().strip() or PORT_DEFAULT
            model = entry_model.get().strip()
            if not model:
                messagebox.showerror("Eroare", "❌ Te rog introdu un model AI!")
                return
            
            try:
                max_tokens = int(entry_tokens.get() or 700)
                timeout = int(entry_timeout.get() or 120)
                
                if max_tokens <= 0 or timeout <= 0:
                    messagebox.showerror("Eroare", "❌ Valorile pentru tokens și timeout trebuie să fie pozitive!")
                    return
                
                machine = {
                    'ip': ip,
                    'port': port,
                    'model': model,
                    'rol': entry_rol.get(),
                    'max_tokens': max_tokens,
                    'timeout': timeout
                }
                
                self.add_machine_to_list(machine)
                self.save_current_config()  # Salvare automată
                messagebox.showinfo("Succes", f"✅ Mașină adăugată și configurație salvată!")
                dialog.destroy()
                
            except ValueError as e:
                messagebox.showerror("Eroare", f"❌ Valori numerice invalide pentru tokens sau timeout!\n{str(e)}")
        
        ctk.CTkButton(
            btn_frame,
            text="✅ Salvează",
            command=save_machine,
            font=("Consolas", 13, "bold"),
            fg_color=COLORS['success'],
            hover_color=COLORS['neon_green'],
            text_color=COLORS['bg_dark'],
            height=40,
            width=150
        ).pack(side='left', padx=10)
        
        ctk.CTkButton(
            btn_frame,
            text="❌ Anulează",
            command=dialog.destroy,
            font=("Consolas", 13, "bold"),
            fg_color=COLORS['error'],
            hover_color='#CC0033',
            text_color=COLORS['text_primary'],
            height=40,
            width=150
        ).pack(side='left', padx=10)
    
    def add_machine_to_list(self, machine: Dict[str, Any]):
        """Adaugă o mașină în listă."""
        self.masini_config.append(machine)
        
        # Create visual card
        card = ctk.CTkFrame(self.machines_frame, fg_color=COLORS['bg_light'], 
                           border_width=2, border_color=COLORS['violet'])
        card.pack(fill='x', padx=5, pady=5)
        
        # Store reference
        machine['widget'] = card
        
        # Header
        header = ctk.CTkFrame(card, fg_color=COLORS['bg_dark'])
        header.pack(fill='x', padx=5, pady=5)
        
        # Icon based on role
        role_icons = {
            'generator': '💡',
            'critic': '🔍',
            'moderator': '⚖️',
            'synth': '🧩'
        }
        icon = role_icons.get(machine['rol'], '🤖')
        
        ctk.CTkLabel(
            header,
            text=f"{icon} Mașină #{len(self.masini_config)} - {machine['rol'].upper()}",
            font=("Consolas", 12, "bold"),
            text_color=COLORS['cyan']
        ).pack(side='left', padx=10, pady=5)
        
        # Select checkbox
        var = tk.BooleanVar()
        machine['selected_var'] = var
        ctk.CTkCheckBox(
            header,
            text="Select",
            variable=var,
            font=("Consolas", 10),
            fg_color=COLORS['violet'],
            hover_color=COLORS['magenta'],
            border_color=COLORS['cyan']
        ).pack(side='right', padx=10, pady=5)
        
        # Details
        details_text = (
            f"🌐 {machine['ip']}:{machine['port']}\n"
            f"🧠 Model: {machine['model']}\n"
            f"📊 Tokens: {machine['max_tokens']} | ⏱️ Timeout: {machine['timeout']}s"
        )
        
        ctk.CTkLabel(
            card,
            text=details_text,
            font=("Consolas", 10),
            text_color=COLORS['text_secondary'],
            justify='left'
        ).pack(anchor='w', padx=15, pady=(0, 10))
        
        # Update counter
        self.machines_info_label.configure(text=f"📊 Total mașini: {len(self.masini_config)}")
    
    def remove_selected_machine(self):
        """Șterge mașinile selectate."""
        to_remove = []
        for machine in self.masini_config:
            if machine.get('selected_var') and machine['selected_var'].get():
                to_remove.append(machine)
        
        if not to_remove:
            messagebox.showinfo("Info", "ℹ️ Nicio mașină selectată!")
            return
        
        # Confirmă ștergerea
        response = messagebox.askyesno(
            "Confirmare", 
            f"⚠️ Ești sigur că vrei să ștergi {len(to_remove)} mașină/mașini?"
        )
        
        if not response:
            return
        
        for machine in to_remove:
            machine['widget'].destroy()
            self.masini_config.remove(machine)
        
        self.machines_info_label.configure(text=f"📊 Total mașini: {len(self.masini_config)}")
        self.save_current_config()  # Salvare automată
        messagebox.showinfo("Succes", f"✅ {len(to_remove)} mașină/mașini șterse și configurație salvată!")
    
    def save_current_config(self):
        """Salvează configurația curentă în JSON (apel intern/automat)."""
        if save_machines_to_json(self.masini_config):
            write_to_log(f"💾 Auto-salvare configurație: {len(self.masini_config)} mașini")
    
    def save_config_manual(self):
        """Salvează manual configurația (apel din buton)."""
        if not self.masini_config:
            messagebox.showwarning("Atenție", "⚠️ Nu există mașini configurate pentru salvare!")
            return
        
        if save_machines_to_json(self.masini_config):
            messagebox.showinfo(
                "Succes", 
                f"✅ Configurație salvată cu succes!\n\n"
                f"📁 Fișier: {CONFIG_FILE}\n"
                f"🤖 Mașini salvate: {len(self.masini_config)}"
            )
        else:
            messagebox.showerror(
                "Eroare", 
                f"❌ Eroare la salvarea configurației!\n\nVerifică log-ul pentru detalii."
            )
    
    def load_config_manual(self):
        """Încarcă manual configurația (apel din buton)."""
        if self.is_running:
            messagebox.showwarning("Atenție", "⚠️ Nu poți încărca configurații în timpul conversației!")
            return
        
        if self.masini_config:
            response = messagebox.askyesno(
                "Confirmare",
                "⚠️ Încărcarea configurației va șterge mașinile curente!\n\n"
                "Continui?"
            )
            if not response:
                return
        
        loaded_machines = load_machines_from_json()
        
        if not loaded_machines:
            messagebox.showerror(
                "Eroare",
                f"❌ Nu s-au putut încărca configurații din {CONFIG_FILE}!\n\n"
                "Verifică dacă fișierul există și este valid."
            )
            return
        
        # Șterge mașinile curente
        for machine in self.masini_config:
            if 'widget' in machine:
                machine['widget'].destroy()
        
        self.masini_config.clear()
        
        # Încarcă noile configurații
        for machine in loaded_machines:
            self.add_machine_to_list(machine)
        
        messagebox.showinfo(
            "Succes",
            f"✅ Configurație încărcată cu succes!\n\n"
            f"📁 Fișier: {CONFIG_FILE}\n"
            f"🤖 Mașini încărcate: {len(loaded_machines)}"
        )
    
    def reset_to_default(self):
        """Resetează la configurația default (2 mașini)."""
        if self.is_running:
            messagebox.showwarning("Atenție", "⚠️ Nu poți reseta configurația în timpul conversației!")
            return
        
        response = messagebox.askyesno(
            "Confirmare Reset",
            "⚠️ Acest lucru va șterge toate mașinile curente\n"
            "și va reveni la cele 2 mașini default!\n\n"
            "Configurația va fi salvată automat.\n\n"
            "Continui?"
        )
        
        if not response:
            return
        
        # Șterge mașinile curente
        for machine in self.masini_config:
            if 'widget' in machine:
                machine['widget'].destroy()
        
        self.masini_config.clear()
        
        # Adaugă mașinile default
        default_machines = get_default_machines()
        for machine in default_machines:
            self.add_machine_to_list(machine)
        
        # Salvează configurația
        self.save_current_config()
        
        messagebox.showinfo(
            "Reset Complet",
            f"✅ Configurație resetată la default!\n\n"
            f"🤖 Mașini configurate: {len(default_machines)}\n"
            f"💾 Configurație salvată în {CONFIG_FILE}"
        )
    
    def on_closing(self):
        """Handler pentru închiderea aplicației - salvează configurația automat."""
        if self.is_running:
            response = messagebox.askyesno(
                "Conversație Activă",
                "⚠️ O conversație este în desfășurare!\n\n"
                "Dacă închizi acum, progresul se va pierde.\n\n"
                "Sigur vrei să închizi aplicația?"
            )
            if not response:
                return
            
            # Oprește conversația
            self.stop_requested = True
        
        # Salvează configurația înainte de închidere
        if self.masini_config:
            self.save_current_config()
            write_to_log("🔚 Aplicație închisă - configurație salvată automat")
        
        # Închide aplicația
        self.quit()
        self.destroy()
    
    def append_output(self, text: str, tag: str = 'content'):
        """Adaugă text în zona de output cu tag specific."""
        self.output_text.insert('end', text, tag)
        self.output_text.see('end')
        self.output_text.update()
    
    def update_status(self, message: str):
        """Actualizează label-ul de status."""
        self.status_label.configure(text=f"⚡ {message}")
        self.status_label.update()
    
    def clear_output(self):
        """Șterge tot conținutul din output."""
        self.output_text.delete('1.0', 'end')
        self.update_status("Status: Output șters · Gata pentru start nou")
    
    def validate_config(self) -> bool:
        """Validează configurația înainte de start."""
        subiect = self.entry_subiect.get().strip()
        if not subiect:
            messagebox.showerror("Eroare", "❌ Te rog introdu o temă de discuție!")
            return False
        
        if len(self.masini_config) < 2:
            messagebox.showerror("Eroare", "❌ Trebuie să configurezi cel puțin 2 mașini AI!")
            return False
        
        try:
            runde = int(self.entry_runde.get() or 0)
            if runde < 0:
                raise ValueError
        except ValueError:
            messagebox.showerror("Eroare", "❌ Număr de runde invalid!")
            return False
        
        try:
            pauza = int(self.entry_pauza.get() or 3)
            if pauza < 0:
                raise ValueError
        except ValueError:
            messagebox.showerror("Eroare", "❌ Pauză invalidă!")
            return False
        
        return True
    
    def start_conversation(self):
        """Pornește conversația în thread separat."""
        if not self.validate_config():
            return
        
        if self.is_running:
            messagebox.showwarning("Warning", "⚠️ Conversația este deja activă!")
            return
        
        # Reset state
        self.is_running = True
        self.stop_requested = False
        self.conversatie = []
        self.registry = {}
        
        # Update UI
        self.btn_start.configure(state='disabled')
        self.btn_stop.configure(state='normal')
        self.update_status("Status: ACTIV · Conversație pornită!")
        self.progress.set(0)
        
        # Start thread
        thread = threading.Thread(target=self.run_conversation, daemon=True)
        thread.start()
    
    def stop_conversation(self):
        """Oprește conversația."""
        if not self.is_running:
            return
        
        self.stop_requested = True
        self.update_status("Status: Oprire solicitată · Se finalizează runda curentă...")
    
    def run_conversation(self):
        """Rulează conversația (funcția main din v4.py adaptată)."""
        try:
            subiect = self.entry_subiect.get().strip()
            numar_runde = int(self.entry_runde.get() or 0)
            pauza = int(self.entry_pauza.get() or 3)
            
            self.append_output("\n" + "="*100 + "\n", 'header')
            self.append_output(f"🚀 CONVERSAȚIE MULTI-AI PORNITĂ\n", 'header')
            self.append_output(f"📝 Tema: {subiect}\n", 'info')
            self.append_output(f"🔄 Runde: {'∞ (infinit)' if numar_runde == 0 else numar_runde}\n", 'info')
            self.append_output(f"⏱️ Pauză: {pauza}s\n", 'info')
            self.append_output(f"🤖 Mașini active: {len(self.masini_config)}\n", 'info')
            self.append_output("="*100 + "\n\n", 'header')
            
            runda = 0
            while True:
                if self.stop_requested:
                    self.append_output("\n❌ OPRIRE SOLICITATĂ DE UTILIZATOR\n", 'error')
                    break
                
                runda += 1
                if numar_runde != 0 and runda > numar_runde:
                    self.append_output(f"\n✅ CONVERSAȚIE FINALIZATĂ după {numar_runde} runde!\n", 'success')
                    break
                
                # Update progress
                if numar_runde > 0:
                    progress_val = runda / numar_runde
                    self.progress.set(progress_val)
                else:
                    # Animație pentru mod infinit
                    self.progress.set((runda % 10) / 10)
                
                self.append_output(f"\n{'▓'*100}\n", 'header')
                self.append_output(f"🔹 RUNDA {runda} 🔹\n", 'header')
                self.append_output(f"{'▓'*100}\n\n", 'header')
                
                write_to_log(f"\n==== RUNDA {runda} ====")
                
                # Parcurgere round-robin
                for idx, m in enumerate(self.masini_config):
                    if self.stop_requested:
                        break
                    
                    rol = m['rol']
                    max_tok = m['max_tokens']
                    timeout = m['timeout']
                    url = f"{m['ip']}:{m['port']}"
                    model = m['model']
                    
                    self.append_output(f"\n🤖 Mașină #{idx+1} [{rol.upper()}] @ {url}\n", 'machine')
                    self.append_output(f"   Model: {model} | Max Tokens: {max_tok} | Timeout: {timeout}s\n", 'info')
                    self.append_output("-"*100 + "\n", 'info')
                    
                    self.update_status(f"Status: Runda {runda} · Mașină #{idx+1} ({rol}) în progres...")
                    
                    idei_list = list(self.registry.keys())
                    mesaje = construieste_mesaje(self.conversatie, subiect, rol, max_tok, idei_list)
                    payload = {
                        "model": model,
                        "messages": mesaje,
                        "stream": True,
                        "options": {"num_predict": max_tok}
                    }
                    
                    # Callbacks
                    def token_callback(token):
                        self.append_output(token, 'content')
                    
                    def status_callback(msg):
                        self.update_status(f"Status: {msg}")
                    
                    rezultat = api_chat_stream(url, payload, timeout, token_callback, status_callback)
                    text = rezultat["text"]
                    tokens = rezultat["tokens"]
                    
                    if text is None:
                        self.append_output(f"\n\n❌ EROARE: Nu s-a primit răspuns de la {url}. OPRIRE.\n", 'error')
                        self.stop_requested = True
                        break
                    
                    self.append_output(f"\n\n💾 Tokens evaluați: {tokens if tokens else 'N/A'}\n", 'info')
                    
                    write_to_log(f"[{rol.upper()} @ {url} :: tokens={tokens}] {text}")
                    
                    # Actualizare registry idei
                    fraze = extrage_fraze_idei(text)
                    adaugate = 0
                    for fr in fraze:
                        if fr not in self.registry:
                            self.registry[fr] = True
                            adaugate += 1
                    
                    # Adaugă în conversație
                    self.conversatie.append({"role": "assistant", "content": text})
                    self.conversatie.append({"role": "user", "content": INTREBARE_IMPLICITA})
                    
                    self.append_output(f"📊 Idei noi în registru: +{adaugate} | Total: {len(self.registry)}\n", 'success')
                    write_to_log(f"[INFO] Idei noi adăugate: {adaugate} | Total registru: {len(self.registry)}")
                    
                    self.append_output("\n" + "─"*100 + "\n", 'info')
                
                if self.stop_requested:
                    break
                
                # Pauză între runde
                self.append_output(f"\n⏸️ Pauză {pauza} secunde înainte de următoarea rundă...\n", 'warning')
                time.sleep(pauza)
            
            # Finalizare
            self.append_output("\n\n" + "="*100 + "\n", 'header')
            self.append_output("🎉 CONVERSAȚIE COMPLETĂ!\n", 'success')
            self.append_output(f"📊 Total idei unice colectate: {len(self.registry)}\n", 'success')
            self.append_output(f"💬 Total mesaje în conversație: {len(self.conversatie)}\n", 'success')
            self.append_output(f"📁 Log salvat în: {LOG_FILE}\n", 'info')
            self.append_output("="*100 + "\n", 'header')
            
            self.update_status("Status: COMPLET · Conversație finalizată cu succes!")
            self.progress.set(1.0)
            
        except Exception as e:
            error_msg = f"❌ EROARE CRITICĂ: {str(e)}\n"
            self.append_output(error_msg, 'error')
            write_to_log(error_msg)
            self.update_status(f"Status: EROARE · {str(e)}")
        
        finally:
            self.is_running = False
            self.btn_start.configure(state='normal')
            self.btn_stop.configure(state='disabled')

# ===== MAIN =====

def main():
    """Punct de intrare în aplicație."""
    app = MultiAIFuturisticGUI()
    
    # Center window
    app.update_idletasks()
    width = app.winfo_width()
    height = app.winfo_height()
    x = (app.winfo_screenwidth() // 2) - (width // 2)
    y = (app.winfo_screenheight() // 2) - (height // 2)
    app.geometry(f'{width}x{height}+{x}+{y}')
    
    app.mainloop()

if __name__ == "__main__":
    main()
