"""Smoke test for license_ui.py WITHOUT a display: tkinter is replaced by
mocks, so this checks the logic (gating, tick, scheduling, dialogs
opening/closing) and catches NameErrors. A real-window check still has to be
done once on Windows."""
import sys, types, tempfile, time, queue
from pathlib import Path
from unittest import mock
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
sys.path.insert(0, str(Path(__file__).resolve().parent))

tk = types.ModuleType("tkinter")
class _W(mock.MagicMock): pass
tk.Toplevel = type("Toplevel", (), {"__init__": lambda self, *a, **k: None,
    "__getattr__": lambda self, n: mock.MagicMock()})
tk.StringVar = lambda value="": mock.MagicMock(get=lambda: value)
tk.TclError = Exception
ttk = types.ModuleType("tkinter.ttk"); ttk.Button = mock.MagicMock; ttk.Frame = mock.MagicMock
ttk.Label = mock.MagicMock; ttk.Entry = mock.MagicMock
mb = types.ModuleType("tkinter.messagebox")
tk.ttk = ttk; tk.messagebox = mb
sys.modules.update({"tkinter": tk, "tkinter.ttk": ttk, "tkinter.messagebox": mb})

from metafixer import license_ui as U
from test_license import Clock, FakeServer, make
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

# badge text
assert U.format_left(1799) == "29:59"
assert U.badge_text({"kind": "trial", "trial_left": 65, "plan": "trial", "expires_at": 0}) == "⏱ Trial 01:05"
assert U.badge_text({"kind": "locked", "plan": "", "expires_at": 0}) == "🔒 Locked"
assert U.badge_text({"kind": "licensed", "plan": "lifetime", "expires_at": 0}) == "✔ Lifetime"
assert U.badge_text({"kind": "licensed", "plan": "month", "expires_at": time.time() + 5 * 86400 + 60}).endswith("5d left")

class FakeApp(U.LicenseMixin):
    def __init__(self, mgr):
        self.lic = mgr; self._lic_events = queue.Queue(); self._lic_dialog = None
        self._lic_hb_running = False; self._lic_last_hb = self._lic_last_refresh = self._lic_last_retry = 0
        self.license_btn = mock.MagicMock(); self.status_var = mock.MagicMock()
        self.worker = None; self.stopped = False; self.dialogs = []
    def _stop_generation(self): self.stopped = True
    def _lic_spawn(self, tag, fn): self._lic_events.put((tag, fn()))
    def _show_license_dialog(self, locked): self.dialogs.append(locked)

clk = Clock(); srv = FakeServer(clk, Ed25519PrivateKey.generate())
m = make(tempfile.mkdtemp(), srv, clk); app = FakeApp(m)
# locked at first (no token) -> gate refuses and opens the lock screen
assert app._license_ok() is False and app.dialogs == [True]
m.refresh()
assert app._license_ok() is True
# tick: heartbeat is scheduled once a minute while on trial
import time as _t
app._lic_last_hb = _t.monotonic() - 100
app._license_tick_inner()
assert app.license_btn.config.called
# trial runs out -> running generation is stopped and the lock screen shows
srv.trial_seconds = 0; clk.adv(61); m.heartbeat()
class Alive: 
    def is_alive(self): return True
app.worker = Alive(); app.dialogs.clear()
app._license_tick_inner()
assert app.stopped and app.dialogs == [True], (app.stopped, app.dialogs)
print("license_ui smoke test passed")
