"""Application entry point for the warehouse desktop tool.

This module wires together the shared async database client, the global
background event loop and the different Tk/CustomTkinter windows exposed by the
project.
"""

import asyncio
import ctypes
import sys
import tkinter as tk

import customtkinter as ctk
from tkinter import messagebox

from async_loop_singleton import get_global_loop
from async_msssql_query import AsyncMSSQLClient, make_mssql_dsn
from gestione_layout import open_layout_window
from gestione_pickinglist import open_pickinglist_window
from login_window import prompt_login
from reset_corsie import open_reset_corsie_window
from search_pallets import open_search_window
from audit_log import log_session_event
from view_celle_multi_udc import open_celle_multiple_window
from user_session import UserSession, create_user_session


# ---- Config ----
SERVER = r"mde3\gesterp"
DBNAME = "Mediseawall"
USER = "sa"
PASSWORD = "1Password1"

# Development shortcut: skip the login dialog and boot directly as MAG1.
# Set to False when you want to restore normal authentication.
BYPASS_LOGIN = True
BYPASS_LOGIN_USER = {
    "operator_id": 4,
    "login": "MAG1",
    "nominativo": "MAG1",
    "privilegio": 3,
    "codice_unita": "U1",
}

if sys.platform.startswith("win"):
    try:
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    except Exception:
        pass

# Create one global loop and make it the default everywhere.
_loop = get_global_loop()
asyncio.set_event_loop(_loop)


def _noop(*args, **kwargs):
    """Compatibility no-op used when optional Tk DPI hooks are missing."""
    return None


if not hasattr(tk.Toplevel, "block_update_dimensions_event"):
    tk.Toplevel.block_update_dimensions_event = _noop  # type: ignore[attr-defined]
if not hasattr(tk.Toplevel, "unblock_update_dimensions_event"):
    tk.Toplevel.unblock_update_dimensions_event = _noop  # type: ignore[attr-defined]

dsn_app = make_mssql_dsn(server=SERVER, database=DBNAME, user=USER, password=PASSWORD)
db_app = AsyncMSSQLClient(dsn_app)
_APP_MUTEX = None
_APP_MUTEX_NAME = "Local\\WarehousePythonAppSingleton"


def _acquire_single_instance_mutex() -> bool:
    """Return ``True`` only for the first running instance of the application."""

    global _APP_MUTEX
    if not sys.platform.startswith("win"):
        return True

    kernel32 = ctypes.windll.kernel32
    mutex = kernel32.CreateMutexW(None, False, _APP_MUTEX_NAME)
    if not mutex:
        return True

    last_error = kernel32.GetLastError()
    _APP_MUTEX = mutex
    ERROR_ALREADY_EXISTS = 183
    return last_error != ERROR_ALREADY_EXISTS


def _build_bypass_session() -> UserSession:
    """Create the development session used when authentication is bypassed."""

    return create_user_session(
        operator_id=int(BYPASS_LOGIN_USER["operator_id"]),
        login=str(BYPASS_LOGIN_USER["login"]),
        nominativo=str(BYPASS_LOGIN_USER["nominativo"]),
        privilegio=int(BYPASS_LOGIN_USER["privilegio"]),
        codice_unita=str(BYPASS_LOGIN_USER["codice_unita"]),
    )


class Launcher(ctk.CTk):
    """Main launcher window that exposes the available warehouse tools."""

    def __init__(self, session: UserSession):
        """Create the launcher toolbar and wire every button to a feature window."""
        super().__init__()
        self.session: UserSession = session
        self.title(f"Warehouse 1.0.0 - {self.session.display_name}")
        self.geometry("1280x96+0+0")

        wrap = ctk.CTkFrame(self)
        wrap.pack(pady=10, fill="x")

        info = ctk.CTkLabel(
            wrap,
            text=f"Operatore: {self.session.display_name} ({self.session.login})",
            anchor="w",
            font=("", 12, "bold"),
        )
        info.grid(row=0, column=0, columnspan=5, padx=6, pady=(4, 2), sticky="ew")

        ctk.CTkButton(
            wrap,
            text="Gestione Corsie",
            state="normal" if self.session.can("launcher.open_reset_corsie") else "disabled",
            command=lambda: open_reset_corsie_window(self, db_app, session=self.session),
        ).grid(row=1, column=0, padx=6, pady=6, sticky="ew")
        ctk.CTkButton(
            wrap,
            text="Gestione Layout",
            state="normal" if self.session.can("launcher.open_layout") else "disabled",
            command=lambda: open_layout_window(self, db_app, session=self.session),
        ).grid(row=1, column=1, padx=6, pady=6, sticky="ew")
        ctk.CTkButton(
            wrap,
            text="UDC Fantasma",
            state="normal" if self.session.can("launcher.open_multi_udc") else "disabled",
            command=lambda: open_celle_multiple_window(self, db_app, session=self.session),
        ).grid(row=1, column=2, padx=6, pady=6, sticky="ew")
        ctk.CTkButton(
            wrap,
            text="Ricerca UDC",
            state="normal" if self.session.can("launcher.open_search") else "disabled",
            command=lambda: open_search_window(self, db_app, session=self.session),
        ).grid(row=1, column=3, padx=6, pady=6, sticky="ew")
        ctk.CTkButton(
            wrap,
            text="Gestione Picking List",
            state="normal" if self.session.can("launcher.open_pickinglist") else "disabled",
            command=lambda: open_pickinglist_window(self, db_app, session=self.session),
        ).grid(row=1, column=4, padx=6, pady=6, sticky="ew")

        for i in range(5):
            wrap.grid_columnconfigure(i, weight=1)

        def _on_close():
            """Dispose shared resources before closing the launcher."""
            try:
                if self.session is not None:
                    log_session_event(self.session, action="logout", outcome="ok")
                fut = asyncio.run_coroutine_threadsafe(db_app.dispose(), _loop)
                try:
                    fut.result(timeout=2)
                except Exception:
                    pass
            finally:
                self.destroy()

        self.protocol("WM_DELETE_WINDOW", _on_close)
        try:
            self.lift()
            self.focus_force()
        except Exception:
            pass


if __name__ == "__main__":
    ctk.set_appearance_mode("light")
    ctk.set_default_color_theme("green")
    if not _acquire_single_instance_mutex():
        root = tk.Tk()
        root.withdraw()
        messagebox.showwarning(
            "Warehouse",
            "L'applicazione e' gia' in esecuzione.\nChiudi l'istanza aperta prima di avviarne un'altra.",
            parent=root,
        )
        try:
            root.destroy()
        except Exception:
            pass
        raise SystemExit(0)

    if BYPASS_LOGIN:
        session = _build_bypass_session()
        log_session_event(
            session,
            action="login.bypass",
            outcome="ok",
            details={"login": session.login},
        )
        bootstrap = None
    else:
        bootstrap = tk.Tk()
        bootstrap.geometry("1x1+0+0")
        bootstrap.overrideredirect(True)
        bootstrap.attributes("-alpha", 0.0)
        bootstrap.deiconify()
        bootstrap.update_idletasks()
        session = prompt_login(bootstrap, db_app)

    if session is None:
        fut = asyncio.run_coroutine_threadsafe(db_app.dispose(), _loop)
        try:
            fut.result(timeout=2)
        except Exception:
            pass
        try:
            if bootstrap is not None:
                bootstrap.destroy()
        except Exception:
            pass
        raise SystemExit(0)

    try:
        if bootstrap is not None:
            bootstrap.destroy()
    except Exception:
        pass

    Launcher(session).mainloop()
