import os
import sys
import subprocess
import tkinter as tk
from tkinter import scrolledtext, messagebox, filedialog, ttk
import threading
import sounddevice as sd
import numpy as np
from scipy.io.wavfile import write
from faster_whisper import WhisperModel
import time
import locale

# --- LIBRARY PATH CONFIGURATION ---
base_dir = os.path.dirname(os.path.abspath(__file__))
lib_dir = os.path.join(base_dir, "lib")

if os.path.exists(lib_dir):
    sys.path.insert(0, lib_dir)

# --- LOAD TRANSLATIONS FROM SEPARATE FILE ---
from translations import LANG_DATA

# Get system language or fallback to English
sys_lang = locale.getdefaultlocale()[0][:2] if locale.getdefaultlocale()[0] else 'en'
T = LANG_DATA.get(sys_lang, LANG_DATA['en'])

# --- PATH CONFIGURATION ---
MODEL_PATH = "/opt/fuss-speechtotext/models"
TEMP_AUDIO = "/tmp/fuss_stt.wav"

class FussListenGUI:
    def __init__(self, root):
        self.root = root
        self.root.title(T['title'])
        self.root.geometry("700x820") 
        self.root.resizable(False, False)
        self.root.configure(bg="#e1f3fe")
        
        # UI Constants
        self.header_bg = "#bdcdd7"
        self.font_bold = ("Arial", 11, "bold")
        
        # State variables
        self.is_recording = False
        self.audio_data = []
        self.model = None
        self.selected_lang = tk.StringVar(value=sys_lang if sys_lang in ['it','de','en','fr'] else "it")

        # --- HEADER SECTION ---
        self.header = tk.Frame(root, bg=self.header_bg, pady=15)
        self.header.pack(fill=tk.X)
        tk.Label(self.header, text=T['title'].upper(), bg=self.header_bg, 
                 fg="#4a5a6a", font=("Arial", 16, "bold")).pack()
        tk.Label(self.header, text=T['subtitle'], 
                 bg=self.header_bg, fg="#4a5a6a", font=("Arial", 9, "bold")).pack()

        # --- LANGUAGE SELECTOR ---
        self.lang_container = tk.Frame(root, bg="#e1f3fe", pady=10)
        self.lang_container.pack(fill=tk.X)
        self.lang_inner = tk.Frame(self.lang_container, bg="#e1f3fe")
        self.lang_inner.pack()
        tk.Label(self.lang_inner, text=T['label_lang'], bg="#e1f3fe", font=self.font_bold, fg="#4a5a6a").pack(side=tk.LEFT, padx=(0, 15))
        
        languages = [("Italiano", "it"), ("Deutsch", "de"), ("English", "en"), ("Français", "fr")]
        for text, val in languages:
            tk.Radiobutton(self.lang_inner, text=text, variable=self.selected_lang, value=val, 
                           bg="#e1f3fe", activebackground="#e1f3fe", font=("Arial", 10), padx=5).pack(side=tk.LEFT)

        # --- MODE SLIDER ---
        self.slider_container = tk.Frame(root, bg="#e1f3fe", pady=5)
        self.slider_container.pack(fill=tk.X)
        self.slider_inner = tk.Frame(self.slider_container, bg="#e1f3fe")
        self.slider_inner.pack()
        tk.Label(self.slider_inner, text=T['label_mode'], bg="#e1f3fe", font=self.font_bold, fg="#4a5a6a").pack(side=tk.LEFT, padx=(0, 15))
        self.beam_slider = tk.Scale(self.slider_inner, from_=1, to=5, orient=tk.HORIZONTAL, 
                                    length=350, bg="#e1f3fe", highlightthickness=0,
                                    showvalue=True, troughcolor="#bdcdd7", activebackground="#6189a6",
                                    command=self.update_description)
        self.beam_slider.set(2)
        self.beam_slider.pack(side=tk.LEFT)
        self.desc_slider = tk.Label(root, text="", bg="#e1f3fe", font=("Arial", 9, "italic"), fg="#7f8c8d")
        self.desc_slider.pack(pady=(0, 5))

        # --- STATUS AND PROGRESS BAR ---
        self.status_label = tk.Label(root, text=T['status_init'], bg="#e1f3fe", 
                                     fg="#d35400", font=self.font_bold)
        self.status_label.pack(pady=5)
        
        self.progress = ttk.Progressbar(root, orient=tk.HORIZONTAL, length=600, mode='determinate')
        self.progress.pack(pady=5)

        # --- TEXT AREA ---
        self.text_frame = tk.Frame(root, bg="#e1f3fe", padx=40)
        self.text_frame.pack(fill=tk.BOTH, expand=True)
        self.text_area = scrolledtext.ScrolledText(self.text_frame, wrap=tk.WORD, font=("Arial", 12), height=10, bd=1, relief="solid")
        self.text_area.pack(fill=tk.BOTH, expand=True)
        
        # Context Menu
        self.context_menu = tk.Menu(self.text_area, tearoff=0)
        self.context_menu.add_command(label=T['menu_cut'], command=lambda: self.text_area.event_generate("<<Cut>>"))
        self.context_menu.add_command(label=T['menu_copy'], command=lambda: self.text_area.event_generate("<<Copy>>"))
        self.context_menu.add_command(label=T['menu_paste'], command=lambda: self.text_area.event_generate("<<Paste>>"))
        self.context_menu.add_separator()
        self.context_menu.add_command(label=T['menu_select_all'], command=self.select_all)
        self.text_area.bind("<Button-3>", lambda e: self.context_menu.tk_popup(e.x_root, e.y_root))

        # --- BUTTONS AREA ---
        self.footer = tk.Frame(root, bg="#e1f3fe", padx=40, pady=25)
        self.footer.pack(fill=tk.X, side=tk.BOTTOM)
        self.footer.columnconfigure(0, weight=1, uniform="group1")
        self.footer.columnconfigure(1, weight=1, uniform="group1")

        self.btn_start = tk.Button(self.footer, text=T['btn_record'], bg="#569d6b", fg="white", font=self.font_bold, relief="flat", command=self.start_recording, state=tk.DISABLED)
        self.btn_start.grid(row=0, column=0, sticky="nsew", padx=30, pady=10, ipady=5)

        self.btn_stop = tk.Button(self.footer, text=T['btn_stop'], bg="#bc6c63", fg="white", font=self.font_bold, relief="flat", command=self.stop_recording, state=tk.DISABLED)
        self.btn_stop.grid(row=0, column=1, sticky="nsew", padx=30, pady=10, ipady=5)

        self.btn_load = tk.Button(self.footer, text=T['btn_load'], bg="#7a8a99", fg="white", font=self.font_bold, relief="flat", command=self.load_file_dialog, state=tk.DISABLED)
        self.btn_load.grid(row=1, column=0, sticky="nsew", padx=30, pady=10, ipady=5)

        self.btn_clear = tk.Button(self.footer, text=T['btn_clear'], bg="#bdcdd7", fg="#4a5a6a", font=self.font_bold, relief="flat", command=self.clear_text)
        self.btn_clear.grid(row=1, column=1, sticky="nsew", padx=30, pady=10, ipady=5)

        # Start AI loading thread
        threading.Thread(target=self.load_model, daemon=True).start()

    def update_description(self, val):
        self.desc_slider.config(text=T.get(f'mode_{val}', ""))

    def select_all(self):
        self.text_area.tag_add("sel", "1.0", "end")
        self.text_area.mark_set("insert", "1.0")
        self.text_area.see("insert")
        return "break"

    def clear_text(self):
        if messagebox.askyesno(T['msg_clear_title'], T['msg_clear_body']):
            self.text_area.delete(1.0, tk.END)

    def load_model(self):
        try:
            self.model = WhisperModel(MODEL_PATH, device="cpu", compute_type="int8", cpu_threads=0, num_workers=2)
            self.root.after(0, self.model_ready)
        except Exception as e:
            self.status_label.config(text=f"{T['status_error']}: {e}", fg="red")

    def model_ready(self):
        self.status_label.config(text=T['status_ready'], fg="#27ae60")
        self.btn_start.config(state=tk.NORMAL)
        self.btn_load.config(state=tk.NORMAL)
        self.update_description(2)

    def start_recording(self):
        self.audio_data = []
        self.is_recording = True
        self.btn_start.config(state=tk.DISABLED, bg="#95a5a6")
        self.btn_stop.config(state=tk.NORMAL)
        self.status_label.config(text=T['status_listening'], fg="#c0392b")
        self.progress['value'] = 0
        threading.Thread(target=self.record_loop, daemon=True).start()

    def record_loop(self):
        try:
            with sd.InputStream(device=None, samplerate=16000, channels=1, callback=lambda i,f,t,s: self.audio_data.append(i.copy())):
                while self.is_recording: sd.sleep(100)
        except Exception as e:
            self.is_recording = False
            self.root.after(0, lambda: messagebox.showerror("Audio Error", str(e)))

    def stop_recording(self):
        self.is_recording = False
        self.btn_stop.config(state=tk.DISABLED)
        self.status_label.config(text=T['status_processing'], fg="#2980b9")
        self.progress.start(10)
        threading.Thread(target=self.process_audio, daemon=True).start()

    def process_audio(self):
        if not self.audio_data: return
        audio = np.concatenate(self.audio_data, axis=0)
        if np.max(np.abs(audio)) < 0.01:
            self.root.after(0, self.audio_silent)
            return
        write(TEMP_AUDIO, 16000, audio)
        self.run_transcription(TEMP_AUDIO)
        if os.path.exists(TEMP_AUDIO): os.remove(TEMP_AUDIO)

    def audio_silent(self):
        self.status_label.config(text=T['status_no_audio'], fg="red")
        self.btn_start.config(state=tk.NORMAL, bg="#569d6b")
        self.progress.stop()

    def load_file_dialog(self):
        path = None
        try:
            cmd = [
                "zenity", "--file-selection", 
                "--title=" + T['zenity_title'],
                "--file-filter=" + T['zenity_filter'] + " (mp3 wav m4a flac ogg) | *.mp3 *.wav *.m4a *.flac *.ogg",
                "--file-filter=All files | *"
            ]
            result = subprocess.run(cmd, capture_output=True, text=True)
            if result.returncode == 0:
                path = result.stdout.strip()
        except FileNotFoundError:
            path = filedialog.askopenfilename(filetypes=[("Audio", "*.mp3 *.wav *.m4a *.flac *.ogg"), ("All files", "*.*")])

        if path and os.path.exists(path):
            self.status_label.config(text=T['status_processing'], fg="#2980b9")
            self.progress.start(10)
            threading.Thread(target=self.run_transcription, args=(path,), daemon=True).start()

    def run_transcription(self, path):
        try:
            lang = self.selected_lang.get()
            v = self.beam_slider.get()
            b_size = v if v <= 3 else (5 if v == 4 else 10)
            patience = 1.0 if v <= 3 else 2.0
            
            segments, _ = self.model.transcribe(
                path, language=lang, beam_size=b_size, patience=patience, 
                condition_on_previous_text=False, initial_prompt="Professional transcription."
            )
            
            full_text = " ".join([s.text for s in segments]).strip()
            # Whisper Hallucination Filter
            for h in ["Grazie a tutti", "Sottotitoli", "Thanks for watching", "Vielen Dank"]:
                if h.lower() in full_text.lower() and len(full_text) < 30: full_text = ""
            
            self.root.after(0, lambda: self.update_ui_post(full_text))
        except Exception as e:
            self.root.after(0, lambda: self.error_transc(e))

    def update_ui_post(self, text):
        if text:
            current_content = self.text_area.get(1.0, tk.END).strip()
            prefix = "\n\n" if current_content else ""
            self.text_area.insert(tk.END, prefix + text)
            self.text_area.see(tk.END)
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.status_label.config(text=T['status_copied'], fg="#27ae60")
        else:
            self.status_label.config(text=T['status_no_text'], fg="#d35400")
        self.progress.stop()
        self.progress['value'] = 100
        self.btn_start.config(state=tk.NORMAL, bg="#569d6b")
        self.btn_load.config(state=tk.NORMAL)

    def error_transc(self, e):
        self.status_label.config(text=T['status_error'], fg="red")
        self.progress.stop()
        messagebox.showerror("AI Error", str(e))

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