# gui_tight.py
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# src/pdflinkcheck/gui_tight.py
from __future__ import annotations
import tkinter as tk
from tkinter import filedialog, ttk, messagebox, PhotoImage
import sys
from pathlib import Path
from typing import Optional
import unicodedata
#from unidecode import unidecode
from importlib.resources import files
import pyhabitat
import ctypes

# --- Core Imports ---
from pdflinkcheck.report import run_report_and_call_exports
from pdflinkcheck.version_info import get_version_from_pyproject
from pdflinkcheck.io import get_first_pdf_in_cwd, get_friendly_path, PDFLINKCHECK_HOME
from pdflinkcheck.environment import pymupdf_is_available, pdfium_is_available, clear_all_caches, is_in_git_repo

class RedirectText:
    """A class to redirect sys.stdout messages to a Tkinter Text widget."""
    def __init__(self, text_widget):
        self.text_widget = text_widget

    def write(self, string):
        self.text_widget.insert(tk.END, string)
        self.text_widget.see(tk.END)
        self.text_widget.update_idletasks()

    def flush(self, *args):
        pass

class PDFLinkCheckerApp(tk.Tk):

    # --- Theme & Visual Initialization ---

    def _initialize_forest_theme(self):
        theme_dir = files("pdflinkcheck.data.themes.forest")
        self.tk.call("source", str(theme_dir / "forest-light.tcl"))
        self.tk.call("source", str(theme_dir / "forest-dark.tcl"))

    def _toggle_theme(self):
        if ttk.Style().theme_use() == "forest-light":
            ttk.Style().theme_use("forest-dark")
        elif ttk.Style().theme_use() == "forest-dark":
            ttk.Style().theme_use("forest-light")

    def _set_icon(self):
        icon_dir = files("pdflinkcheck.data.icons")
        try:
            png_path = icon_dir.joinpath("Logo-150x150.png")
            if png_path.exists():
                self.icon_img = PhotoImage(file=str(png_path))
                self.iconphoto(True, self.icon_img)
        except Exception:
            pass
        try:
            icon_path = icon_dir.joinpath("red_pdf_512px.ico")
            if icon_path.exists():
                self.iconbitmap(str(icon_path))
        except Exception:
            pass

    # --- Lifecycle & Initialization ---

    def __init__(self):
        super().__init__()

        self._initialize_forest_theme()

        # Tighten global padding and font consistency (font size unchanged)
        style = ttk.Style()
        style.configure(".", padding=2)                # global min padding
        style.configure("TFrame", padding=2)
        style.configure("TLabelFrame", padding=(4,2))
        style.configure("TButton", padding=4)
        style.configure("TCheckbutton", padding=2)
        style.configure("TRadiobutton", padding=2)

        ttk.Style().theme_use("forest-dark")

        self.title(f"PDF Link Check v{get_version_from_pyproject()}")  # Short title
        self.geometry("700x500")  # Smaller starting size
        self.minsize(600, 400)    # Prevent too-small window

        self._set_icon()

        # --- 1. Variable State Management ---
        self.pdf_path = tk.StringVar(value="")
        self.pdf_library_var = tk.StringVar(value="PDFium")
        self.do_export_report_json_var = tk.BooleanVar(value=True)
        self.do_export_report_txt_var = tk.BooleanVar(value=True)
        self.current_report_text = None
        self.current_report_data = None

        # Track exported file paths
        self.last_json_path: Optional[Path] = None
        self.last_txt_path: Optional[Path] = None

        if not pdfium_is_available():
            self.pdf_library_var.set("PyMuPDF")
        if not pymupdf_is_available():
            self.pdf_library_var.set("pypdf")

        # --- 2. Widget Construction ---
        self._create_widgets()
        self._initialize_menubar()

    def _initialize_menubar(self):
        """Builds the application menu bar."""
        menubar = tk.Menu(self)
        self.config(menu=menubar)

        tools_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Tools", menu=tools_menu)

        tools_menu.add_command(label="Toggle Theme", command=self._toggle_theme)
        tools_menu.add_command(label="Clear Output Window", command=self._clear_output_window)
        tools_menu.add_command(label="Copy Output to Clipboard", command=self._copy_output_to_clipboard)
        tools_menu.add_command(label="Clear Cache", command=self._clear_all_caches)

        tools_menu.add_separator()
        tools_menu.add_command(label="License", command=self._show_license)
        tools_menu.add_command(label="Readme", command=self._show_readme)
        tools_menu.add_command(label="I Have Questions", command=self._show_i_have_questions)

    # --- UI Component Building ---

    def _create_widgets(self):
        """Compact layout with reduced padding."""

        # --- Control Frame (Top) ---
        control_frame = ttk.Frame(self, padding=(4, 2, 4, 2))
        control_frame.pack(fill='x', pady=(2, 2))

        # === Row 0: File Selection ===
        file_selection_frame = ttk.Frame(control_frame)
        file_selection_frame.grid(row=0, column=0, columnspan=3, padx=0, pady=(2, 4), sticky='ew')

        ttk.Label(file_selection_frame, text="PDF Path:").pack(side=tk.LEFT, padx=(0, 3))
        entry = ttk.Entry(file_selection_frame, textvariable=self.pdf_path)
        entry.pack(side=tk.LEFT, fill='x', expand=True, padx=3)
        ttk.Button(file_selection_frame, text="Browse...", command=self._select_pdf, width=10).pack(side=tk.LEFT, padx=(3, 3))
        ttk.Button(file_selection_frame, text="Copy Path", command=self._copy_pdf_path, width=10).pack(side=tk.LEFT, padx=(0, 0))

        # === Row 1: Configuration & Export Jumps ===
        pdf_library_frame = ttk.LabelFrame(control_frame, text="Backend Engine:")
        pdf_library_frame.grid(row=1, column=0, padx=3, pady=3, sticky='nsew')

        if pdfium_is_available():
            ttk.Radiobutton(pdf_library_frame, text="PDFium", variable=self.pdf_library_var, value="PDFium").pack(side='left', padx=5, pady=1) 
        if pymupdf_is_available():
            ttk.Radiobutton(pdf_library_frame, text="PyMuPDF", variable=self.pdf_library_var, value="PyMuPDF").pack(side='left', padx=3, pady=1)
        ttk.Radiobutton(pdf_library_frame, text="pypdf", variable=self.pdf_library_var, value="pypdf").pack(side='left', padx=3, pady=1)

        export_config_frame = ttk.LabelFrame(control_frame, text="Export Enabled:")
        export_config_frame.grid(row=1, column=1, padx=3, pady=3, sticky='nsew')

        ttk.Checkbutton(export_config_frame, text="JSON", variable=self.do_export_report_json_var).pack(side=tk.LEFT, padx=4)
        ttk.Checkbutton(export_config_frame, text="TXT", variable=self.do_export_report_txt_var).pack(side=tk.LEFT, padx=4)

        self.export_actions_frame = ttk.LabelFrame(control_frame, text="Open Report Files:")
        self.export_actions_frame.grid(row=1, column=2, padx=3, pady=3, sticky='nsew')

        self.btn_open_json = ttk.Button(self.export_actions_frame, text="Open JSON", command=lambda: self._open_export_file("json"), width=10)
        self.btn_open_json.pack(side=tk.LEFT, padx=3, pady=1)

        self.btn_open_txt = ttk.Button(self.export_actions_frame, text="Open TXT", command=lambda: self._open_export_file("txt"), width=10)
        self.btn_open_txt.pack(side=tk.LEFT, padx=3, pady=1)

        # === Row 3: Action Buttons ===
        run_analysis_btn = ttk.Button(control_frame, text="▶ Run Analysis", command=self._run_report_gui, style='Accent.TButton', width=16)
        run_analysis_btn.grid(row=3, column=0, columnspan=2, pady=6, sticky='ew', padx=(0, 3))

        clear_window_btn = ttk.Button(control_frame, text="Clear Output Window", command=self._clear_output_window, width=18)
        clear_window_btn.grid(row=3, column=2, pady=6, sticky='ew', padx=3)

        # Grid configuration
        control_frame.grid_columnconfigure(0, weight=1)
        control_frame.grid_columnconfigure(1, weight=1)
        control_frame.grid_columnconfigure(2, weight=1)

        # --- Output Frame (Bottom) ---
        output_frame = ttk.Frame(self, padding=(4, 2, 4, 4))
        output_frame.pack(fill='both', expand=True)

        output_header_frame = ttk.Frame(output_frame)
        output_header_frame.pack(fill='x', pady=(0, 1))

        ttk.Label(output_header_frame, text="Analysis Report Logs:").pack(side=tk.LEFT, fill='x', expand=True)

        ttk.Button(output_header_frame, text="▼ Bottom", command=self._scroll_to_bottom, width=10).pack(side=tk.RIGHT, padx=(0, 2))
        ttk.Button(output_header_frame, text="▲ Top", command=self._scroll_to_top, width=6).pack(side=tk.RIGHT, padx=2)

        # Scrollable Text Area
        text_scroll_frame = ttk.Frame(output_frame)
        text_scroll_frame.pack(fill='both', expand=True, padx=3, pady=3)

        self.output_text = tk.Text(text_scroll_frame, wrap=tk.WORD, state=tk.DISABLED, bg='#2b2b2b', fg='#ffffff', font=('Monospace', 10))
        self.output_text.pack(side=tk.LEFT, fill='both', expand=True)

        scrollbar = ttk.Scrollbar(text_scroll_frame, command=self.output_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.output_text['yscrollcommand'] = scrollbar.set

    # --- Event Handlers & Business Logic ---

    def _select_pdf(self):
        if self.pdf_path.get():
            initialdir = str(Path(self.pdf_path.get()).parent)
        elif pyhabitat.is_msix():
            initialdir = str(Path.home())
        else:
            initialdir = str(Path.cwd())

        file_path = filedialog.askopenfilename(
            initialdir=initialdir,
            defaultextension=".pdf",
            filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")]
        )
        if file_path:
            self.pdf_path.set(get_friendly_path(file_path))

    def _copy_pdf_path(self):
        path_to_copy = self.pdf_path.get()
        if path_to_copy:
            try:
                self.clipboard_clear()
                self.clipboard_append(path_to_copy)
                messagebox.showinfo("Copied", "PDF Path copied to clipboard.")
            except tk.TclError as e:
                messagebox.showerror("Copy Error", f"Clipboard access blocked: {e}")
        else:
            messagebox.showwarning("Copy Failed", "PDF Path field is empty.")

    def _run_report_gui(self):
        pdf_path_str = self._assess_pdf_path_str()
        if not pdf_path_str:
            return

        export_format = ""
        if self.do_export_report_json_var.get():
            export_format += "JSON"
        if self.do_export_report_txt_var.get():
            export_format += "TXT"

        pdf_library = self.pdf_library_var.get().lower()

        self.output_text.config(state=tk.NORMAL)
        self.output_text.delete('1.0', tk.END)

        original_stdout = sys.stdout
        sys.stdout = RedirectText(self.output_text)

        try:
            report_results = run_report_and_call_exports(
                pdf_path=pdf_path_str,
                export_format=export_format,
                pdf_library=pdf_library,
            )
            self.current_report_text = report_results.get("text", "")
            self.current_report_data = report_results.get("data", {})

            self.last_json_path = report_results.get("files", {}).get("export_path_json")
            self.last_txt_path = report_results.get("files", {}).get("export_path_txt")

        except Exception as e:
            messagebox.showinfo(
                "Engine Fallback",
                f"Error encountered with {pdf_library}: {e}\n\nFalling back to pypdf."
            )
            self.pdf_library_var.set("pypdf")
        finally:
            sys.stdout = original_stdout
            self.output_text.config(state=tk.DISABLED)

    def _open_export_file(self, file_type: str):
        target_path = self.last_json_path if file_type == "json" else self.last_txt_path

        if target_path and Path(target_path).exists():
            try:
                pyhabitat.edit_textfile(target_path)
            except Exception as e:
                messagebox.showerror("Open Error", f"Failed to open {file_type.upper()} report: {e}")
        else:
            messagebox.showwarning("File Not Found", f"The {file_type.upper()} report file does not exist.\n\nPlease click 'Run Analysis' to generate one.")

    def _assess_pdf_path_str(self):
        pdf_path_str = self.pdf_path.get().strip()
        if not pdf_path_str:
            pdf_path_str = get_first_pdf_in_cwd()
            if not pdf_path_str:
                self._display_error("No PDF found in current directory.")
                return

        p = Path(pdf_path_str).expanduser().resolve()
        if not p.exists():
            self._display_error(f"PDF file not found at: {p}")
            return

        return str(p)

    # --- Utility Methods ---

    def _clear_output_window(self):
        self.output_text.config(state=tk.NORMAL)
        self.output_text.delete('1.0', tk.END)
        self.output_text.config(state=tk.DISABLED)

    def _copy_output_to_clipboard(self):
        content = self.output_text.get('1.0', tk.END)
        self.clipboard_clear()
        self.clipboard_append(content)
        messagebox.showinfo("Clipboard", "Output buffer copied to clipboard.")

    def _scroll_to_top(self):
        self.output_text.see('1.0')

    def _scroll_to_bottom(self):
        self.output_text.see(tk.END)

    def _clear_all_caches(self):
        clear_all_caches()
        messagebox.showinfo("Caches Cleared", f"All caches have been cleared.\nPyMuPDF available: {pymupdf_is_available()}\nPDFium available: {pdfium_is_available()}")

    def _display_error(self, message):
        self.output_text.config(state=tk.NORMAL)
        self.output_text.insert(tk.END, f"[ERROR] {message}\n", 'error')
        self.output_text.tag_config('error', foreground='red')
        self.output_text.see(tk.END)
        self.output_text.config(state=tk.DISABLED)

    # --- Modal Documentation Windows ---

    def _show_license(self):
        self._display_resource_window("LICENSE", "Software License")

    def _show_readme(self):
        self._display_resource_window("README.md", "pdflinkcheck README.md")

    def _show_i_have_questions(self):
        self._display_resource_window("I Have Questions.md", "I Have Questions.md")

    def _display_resource_window(self, filename: str, title: str):
        content = None
        try:
            content = (files("pdflinkcheck.data") / filename).read_text(encoding="utf-8")
        except FileNotFoundError:
            if is_in_git_repo():
                messagebox.showinfo("Development Mode", f"Embedded {filename} not found.\nTrying to copy from project root...")
                try:
                    from pdflinkcheck.datacopy import ensure_data_files_for_build
                    ensure_data_files_for_build()
                    content = (files("pdflinkcheck.data") / filename).read_text(encoding="utf-8")
                except ImportError:
                    messagebox.showerror("Fallback Failed", "Cannot import datacopy module.")
                    return
                except Exception as e:
                    messagebox.showerror("Copy Failed", f"Failed to copy {filename}: {e}")
                    return
            else:
                messagebox.showerror("Resource Missing", f"Embedded file '{filename}' not found.")
                return
        except Exception as e:
            messagebox.showerror("Read Error", f"Failed to read {filename}: {e}")
            return

        content = sanitize_glyphs_for_tkinter(content)

        win = tk.Toplevel(self)
        win.title(title)
        win.geometry("700x500")

        txt = tk.Text(win, wrap=tk.WORD, font=('Monospace', 10), padx=6, pady=6)
        txt.insert(tk.END, content)
        txt.config(state=tk.DISABLED)

        sb = ttk.Scrollbar(win, command=txt.yview)
        txt['yscrollcommand'] = sb.set

        sb.pack(side=tk.RIGHT, fill=tk.Y)
        txt.pack(fill='both', expand=True)

        win.transient(self)
        win.grab_set()

# --- Helper Functions ---
def sanitize_glyphs_for_tkinter_(text: str) -> str:
    """
    Convert text to ASCII-friendly form for Tkinter Text widget.
    Keeps readability: e.g., “é” → "e", bullets → "*", fancy quotes → '"' 
    Collapses multiple spaces and replaces non-breaking spaces.
    """
    if not text:
        return ""
    
    # Transliterate Unicode characters to closest ASCII equivalents
    sanitized = unidecode(text)
    
    # Replace non-breaking spaces with normal spaces
    sanitized = sanitized.replace('\xa0', ' ')
    
    # Collapse multiple spaces
    sanitized = ' '.join(sanitized.split())
    
    return sanitized
def sanitize_glyphs_for_tkinter(text: str) -> str:
    normalized = unicodedata.normalize('NFKD', text)
    sanitized = normalized.encode('ascii', 'ignore').decode('utf-8')
    return sanitized.replace('  ', ' ')

def start_gui(time_auto_close: int = 0):
    print("pdflinkcheck: start_gui ...")
    app = PDFLinkCheckerApp()

    app.lift()
    app.wm_attributes("-topmost", True)
    app.after(200, lambda: app.wm_attributes("-topmost", False))
    app.focus_force()

    if pyhabitat.on_windows():
        hwnd = app.winfo_id()
        ctypes.windll.user32.SetForegroundWindow(hwnd)

    if time_auto_close > 0:
        app.after(time_auto_close, app.destroy)

    app.mainloop()
    print("pdflinkcheck: gui closed.")

if __name__ == "__main__":
    start_gui()
