import warnings
warnings.filterwarnings("ignore")

# Fix blurry / tiny fonts on Windows high-DPI displays.
# This must run before creating the Tk root window.
def enable_windows_dpi_awareness():
    """Use Per-Monitor V2 DPI awareness when Windows supports it.

    This keeps the UI sharper on 125% / 150% / 200% displays and avoids
    Windows bitmap-scaling the whole Tk window. The app itself still clamps
    Tk font scaling later so layout stays stable on other computers.
    """
    try:
        import ctypes
        # Windows 10 1703+: PER_MONITOR_AWARE_V2 = -4
        try:
            ctypes.windll.user32.SetProcessDpiAwarenessContext(ctypes.c_void_p(-4))
            return
        except Exception:
            pass
        # Windows 8.1+: PROCESS_PER_MONITOR_DPI_AWARE = 2
        try:
            ctypes.windll.shcore.SetProcessDpiAwareness(2)
            return
        except Exception:
            pass
        # Windows 7 fallback
        try:
            ctypes.windll.user32.SetProcessDPIAware()
        except Exception:
            pass
    except Exception:
        pass

enable_windows_dpi_awareness()

import tkinter as tk
import atexit
import socket
import threading
from pathlib import Path
from app import OfficeAssistantApp
from core.constants import APP_VERSION, BASE_DIR

SINGLE_INSTANCE_HOST = "127.0.0.1"
SINGLE_INSTANCE_PORT = 43587
SINGLE_INSTANCE_TOKEN = "NexaOffice.WorkSuite.Desktop.v38"


def app_user_model_id() -> str:
    """Return a stable Windows identity derived from the app version.

    Keeping this dynamic prevents a stale taskbar identity after version bumps,
    which can otherwise make Windows keep an old icon/cache group.
    """
    return "NexaOffice.WorkSuite.Desktop." + APP_VERSION.replace(".", "")


def single_instance_or_signal_existing():
    """Return a listening socket for the first instance, or signal it and exit.

    This prevents users from accidentally starting a second app when the first
    one is minimized. The second EXE exits before Tk is created, which also
    avoids duplicate-window stutter and black flash on Windows.
    """
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        if hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
        else:
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    except Exception:
        pass
    try:
        sock.bind((SINGLE_INSTANCE_HOST, SINGLE_INSTANCE_PORT))
        sock.listen(3)
        atexit.register(lambda: sock.close())
        return sock
    except OSError:
        try:
            with socket.create_connection((SINGLE_INSTANCE_HOST, SINGLE_INSTANCE_PORT), timeout=0.45) as client:
                client.sendall((SINGLE_INSTANCE_TOKEN + "\nSHOW\n").encode("utf-8"))
        except Exception:
            pass
        try:
            sock.close()
        except Exception:
            pass
        return None


def start_single_instance_listener(listener, root, app):
    def worker():
        while True:
            try:
                conn, _addr = listener.accept()
            except Exception:
                break
            with conn:
                try:
                    payload = conn.recv(256).decode("utf-8", errors="ignore")
                except Exception:
                    payload = ""
                if SINGLE_INSTANCE_TOKEN in payload and "SHOW" in payload:
                    try:
                        root.after(0, app.restore_window_from_second_instance)
                    except Exception:
                        pass
    threading.Thread(target=worker, daemon=True).start()



def configure_windows_app_identity():
    """Make Windows taskbar / Alt-Tab use the NexaOffice identity.

    PyInstaller uses assets/nexaoffice.ico for the EXE file icon, while this
    function helps Windows group the window under the same NexaOffice identity.
    """
    try:
        import ctypes
        ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_user_model_id())
    except Exception:
        pass


def apply_root_icon(root):
    """Apply the NexaOffice icon to the running Tk window.

    On Windows we set BOTH iconbitmap(.ico) and iconphoto(PNG). Some Tk / DPI
    combinations only refresh the taskbar icon after iconphoto is set, while
    EXE packaging uses the embedded .ico from the PyInstaller spec.
    """
    ico_path = BASE_DIR / "assets" / "nexaoffice.ico"
    try:
        if ico_path.exists():
            try:
                root.iconbitmap(default=str(ico_path))
            except TypeError:
                root.iconbitmap(str(ico_path))
    except Exception:
        pass
    try:
        from PIL import Image, ImageTk
        refs = []
        # v2026.06.20.78: restore the original Nexa mark and offer a
        # balanced size order so Tk/Windows can pick crisp small frames first
        # without replacing the logo design itself.
        for size in (32, 24, 20, 16, 48, 64, 96, 128, 256):
            icon_path = BASE_DIR / "assets" / f"nexaoffice_icon_{size}.png"
            if icon_path.exists():
                refs.append(ImageTk.PhotoImage(Image.open(icon_path).convert("RGBA")))
        if refs:
            root._nexaoffice_runtime_icon = refs
            root.iconphoto(True, *refs)
    except Exception:
        pass


def main():
    configure_windows_app_identity()
    listener = single_instance_or_signal_existing()
    if listener is None:
        return
    root = tk.Tk()
    apply_root_icon(root)
    # Windows may not lock the taskbar/titlebar icon until the window exists.
    # Refresh once shortly after mapping so Python-source runs match packaged EXE.
    root.after(380, lambda: apply_root_icon(root))
    app = OfficeAssistantApp(root)
    root.after(1100, lambda: apply_root_icon(root))
    start_single_instance_listener(listener, root, app)
    root.mainloop()


if __name__ == "__main__":
    main()
