#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Holyrics Sync - App que roda no PC do Holyrics e recebe vídeos prontos
do painel Igreja Connect, salvando direto na pasta de vídeos do Holyrics.

COMO VIRAR .EXE (no seu PC Windows):
1) Instale Python 3 (python.org) - marque "Add to PATH"
2) pip install pyinstaller pystray pillow
3) Rode: pyinstaller --onefile --noconsole holyrics_client.py
4) O .exe fica em dist/holyrics_client.exe

FLUXO:
1) Abre o app, define senha de 6 dígitos e link do painel
2) Cadastra a senha no painel (aba Holyrics) e liga ao grupo
3) Todo vídeo pronto naquele grupo chega sozinho na pasta de vídeos
"""

import hashlib
import json
import os
import queue
import shutil
import sys
import threading
import urllib.error
import urllib.parse
import urllib.request
import uuid
import webbrowser
from pathlib import Path

show_setup = "--setup" in sys.argv or "-s" in sys.argv

if show_setup:
    HAS_TRAY = False
else:
    try:
        import pystray
        from PIL import Image, ImageDraw
        HAS_TRAY = True
    except ImportError:
        HAS_TRAY = False

try:
    import tkinter as tk
    from tkinter import messagebox
    HAS_TK = True
except ImportError:
    HAS_TK = False

APP_NAME = "HolyricsSync"
SALT = "holyrics-sync-v1"
DEFAULT_FOLDER = r"C:\Holyrics\Holyrics\files\media\video"
DEFAULT_IMAGE_FOLDER = r"C:\Holyrics\Holyrics\files\media\image"
POLL_SECONDS = 5
STARTUP_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run"


def config_dir():
    base = os.environ.get("APPDATA") or os.path.expanduser("~")
    d = os.path.join(base, APP_NAME)
    os.makedirs(d, exist_ok=True)
    return d


CONFIG = os.path.join(config_dir(), "config.json")
FIRST_RUN_FLAG = os.path.join(config_dir(), ".first_run_done")


def hash_pin(pin):
    return hashlib.sha256((SALT + str(pin or "").strip()).encode()).hexdigest()


def sanitize(name):
    name = str(name or "video").strip()
    for ch in '\\/:*?"<>|':
        name = name.replace(ch, "_")
    return (name[:120] or "video")


def load_config():
    try:
        with open(CONFIG, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return None


def save_config(cfg):
    with open(CONFIG, "w", encoding="utf-8") as f:
        json.dump(cfg, f, ensure_ascii=False, indent=2)


def http_post(server, path, payload, timeout=15):
    url = server.rstrip("/") + path
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        url, data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode("utf-8"))


def http_get(server, path, dest, timeout=600):
    url = server.rstrip("/") + path
    with urllib.request.urlopen(url, timeout=timeout) as resp, open(dest, "wb") as f:
        shutil.copyfileobj(resp, f)


def is_in_startup():
    try:
        import winreg
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, STARTUP_KEY, 0, winreg.KEY_READ)
        winreg.QueryValueEx(key, APP_NAME)
        winreg.CloseKey(key)
        return True
    except Exception:
        return False


def add_to_startup(exe_path):
    try:
        import winreg
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, STARTUP_KEY, 0, winreg.KEY_SET_VALUE)
        winreg.SetValueEx(key, APP_NAME, 0, winreg.REG_SZ, f'"{exe_path}" --minimized')
        winreg.CloseKey(key)
        return True
    except Exception:
        return False


def remove_from_startup():
    try:
        import winreg
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, STARTUP_KEY, 0, winreg.KEY_SET_VALUE)
        winreg.DeleteValue(key, APP_NAME)
        winreg.CloseKey(key)
        return True
    except Exception:
        return False


class SyncWorker(threading.Thread):
    def __init__(self, cfg, log_fn, status_fn):
        super().__init__(daemon=True)
        self.cfg = cfg
        self.log = log_fn
        self.status_fn = status_fn
        self._stop = threading.Event()
        self.registered = False
        self.agent = None
        self.error = None
        self._last_sig = None

    def stop(self):
        self._stop.set()

    def _set_error(self, err, msg):
        self.error = err
        self.registered = False
        sig = ("error",)
        if self._last_sig != sig:
            self._last_sig = sig
            self.log(msg)
            self.status_fn("error", msg)

    def run(self):
        while not self._stop.wait(POLL_SECONDS):
            try:
                self.tick()
            except urllib.error.HTTPError as e:
                self._set_error(f"HTTP {e.code}", f"Erro HTTP {e.code}. Confira o link do painel.")
            except (urllib.error.URLError, OSError) as e:
                self._set_error(str(e), f"Sem conexão com o painel: {e}")
            except Exception as e:
                self._set_error(str(e), f"Erro: {e}")

    def tick(self):
        cfg = self.cfg
        r = http_post(
            cfg["server"], "/agent/ping",
            {"clientId": cfg["clientId"], "pinHash": cfg["pinHash"]},
        )
        if not r.get("registered"):
            self.registered = False
            self.agent = None
            self.error = None
            sig = ("pending",)
            if self._last_sig != sig:
                self._last_sig = sig
                self.log("Aguardando cadastro no painel (senha de 6 dígitos)...")
                self.status_fn("pending", "Aguardando cadastro no painel")
            return
        self.registered = True
        self.error = None
        self.agent = r.get("agent") or {}
        name = self.agent.get("name") or "cliente registrado"
        group = self.agent.get("groupName")
        sig = ("ok", name, group)
        if self._last_sig != sig:
            self._last_sig = sig
            msg = f"Conectado como {name}" + (f" → {group}" if group else "")
            self.log(msg)
            self.status_fn("connected", msg)
        for cmd in r.get("commands") or []:
            if cmd.get("type") == "download":
                self.process_download(cmd)

    def process_download(self, cmd):
        did = str(cmd.get("id") or "")
        if not did:
            return

        kind = (cmd.get("kind") or "video").lower()
        is_image = kind == "image" or kind == "media"  # media pode ser vídeo ou imagem

        if is_image:
            folder = (self.cfg.get("imageFolder") or DEFAULT_IMAGE_FOLDER).strip()
            default_ext = ".jpg"
            media_type = "imagem"
        else:
            folder = (self.cfg.get("folder") or DEFAULT_FOLDER).strip()
            default_ext = ".mp4"
            media_type = "vídeo"

        try:
            os.makedirs(folder, exist_ok=True)
        except OSError as e:
            self.log(f"Não consegui criar a pasta: {e}")
            self.confirm(did, False, f"Pasta inválida: {e}")
            return

        src_name = cmd.get("filename") or cmd.get("title") or media_type
        base, ext = os.path.splitext(os.path.basename(str(src_name)))
        ext = (ext or default_ext).lower()
        target = os.path.join(folder, f"{sanitize(base)}_{did[:6]}{ext}")
        tmp = target + ".part"
        self.log(f"Baixando {media_type} {base}{ext} → {folder}")
        try:
            query = urllib.parse.urlencode({"clientId": self.cfg["clientId"], "pinHash": self.cfg["pinHash"]})
            http_get(self.cfg["server"], f"/agent/file/{urllib.parse.quote(did)}?{query}", tmp)
            os.replace(tmp, target)
            self.log(f"Salvo no Holyrics: {os.path.basename(target)} ({media_type})")
            self.confirm(did, True)
        except Exception as e:
            self.log(f"Falha ao salvar o {media_type}: {e}")
            try:
                if os.path.exists(tmp):
                    os.remove(tmp)
            except OSError:
                pass
            self.confirm(did, False, str(e))

    def confirm(self, did, ok, error=""):
        try:
            http_post(
                self.cfg["server"], "/agent/confirm",
                {"clientId": self.cfg["clientId"], "pinHash": self.cfg["pinHash"],
                 "id": did, "ok": ok, "error": error},
            )
        except Exception as e:
            self.log(f"Não consegui confirmar o envio: {e}")


def create_tray_icon():
    img = Image.new('RGBA', (64, 64), (11, 18, 32, 0))
    draw = ImageDraw.Draw(img)
    draw.rounded_rectangle([8, 8, 56, 56], radius=12, fill="#0b1220", outline="#22c55e", width=3)
    draw.text((32, 32), "H", fill="#22c55e", anchor="mm", font_size=28)
    return img


class TrayApp:
    def __init__(self):
        self.cfg = load_config()
        self.worker = None
        self.icon = None
        self.status = "disconnected"
        self.status_msg = "Desconectado"
        self.setup_window = None
        self.card_window = None

    def run(self):
        if not self.cfg:
            self.show_setup()
            return

        if not os.path.exists(FIRST_RUN_FLAG):
            self.ask_startup()

        self.start_worker()
        # Iniciar em modo normal (janela), não na bandeja automaticamente
        # O usuário pode minimizar manualmente depois
        self.run_normal()

    def ask_startup(self):
        if not HAS_TK:
            self.mark_first_run_done()
            return

        root = tk.Tk()
        root.withdraw()
        exe_path = sys.executable
        if getattr(sys, 'frozen', False):
            exe_path = sys.executable
        else:
            exe_path = os.path.abspath(sys.argv[0])

        result = messagebox.askyesno(
            "Holyrics Sync",
            "Deseja que o Holyrics Sync inicie automaticamente com o Windows?\n\n"
            "Assim ele já fica rodando na bandeja do sistema (ícones ocultos) "
            "e recebe os vídeos assim que o computador ligar.",
            icon="question"
        )
        if result:
            add_to_startup(exe_path)
        self.mark_first_run_done()
        root.destroy()

    def mark_first_run_done(self):
        try:
            with open(FIRST_RUN_FLAG, "w") as f:
                f.write("done")
        except Exception:
            pass

    def start_worker(self):
        if self.worker and self.worker.is_alive():
            return
        self.worker = SyncWorker(self.cfg, self.log, self.on_status_change)
        self.worker.start()

    def stop_worker(self):
        if self.worker and self.worker.is_alive():
            self.worker.stop()
            self.worker.join(timeout=2)
        self.worker = None

    def log(self, msg):
        print(f"[HolyricsSync] {msg}")

    def on_status_change(self, status, msg):
        self.status = status
        self.status_msg = msg
        if self.icon:
            self.update_tray_tooltip()

    def update_tray_tooltip(self):
        if self.icon:
            if self.status == "connected":
                self.icon.title = f"Holyrics Sync - Conectado"
            elif self.status == "pending":
                self.icon.title = f"Holyrics Sync - Aguardando cadastro"
            elif self.status == "error":
                self.icon.title = f"Holyrics Sync - Erro"
            else:
                self.icon.title = "Holyrics Sync"

    def run_tray(self):
        icon_image = create_tray_icon()
        menu = pystray.Menu(
            pystray.MenuItem("Status: " + self.status_msg, None, enabled=False),
            pystray.Menu.SEPARATOR,
            pystray.MenuItem("Mostrar PIN / Status", self.show_card, default=True),
            pystray.MenuItem("Configurações", self.show_setup),
            pystray.Menu.SEPARATOR,
            pystray.MenuItem("Iniciar com Windows", self.toggle_startup, checked=lambda item: is_in_startup()),
            pystray.MenuItem("Sair", self.quit_app),
        )
        self.icon = pystray.Icon(APP_NAME, icon_image, "Holyrics Sync", menu)
        self.icon.run()

    def run_fallback(self):
        if HAS_TK:
            root = tk.Tk()
            root.withdraw()
            messagebox.showinfo("Holyrics Sync", "Rodando em segundo plano.\nVerifique a bandeja do sistema.")
            root.destroy()
        import time
        while True:
            time.sleep(60)

    def run_normal(self):
        """Start in normal windowed mode, allow minimize to tray"""
        if not HAS_TK:
            return
        
        root = tk.Tk()
        root.withdraw()  # Esconder initially
        
        # Create a simple window
        root.title("Holyrics Sync")
        root.geometry("300x200")
        root.configure(bg="#0b1220")
        root.resizable(False, False)
        
        label = tk.Label(root, text="Holyrics Sync\nRodando em segundo plano...", 
                        bg="#0b1220", fg="#eaf0ff", font=("Segoe UI", 14))
        label.pack(expand=True)
        
        # Bind minimize to hide to tray
        root.protocol("WM_DELETE_WINDOW", lambda: self.minimize_to_tray(root))
        
        # Try to create tray icon in background
        try:
            import pystray
            from PIL import Image, ImageDraw
            
            icon_image = create_tray_icon()
            tray_menu = pystray.Menu(
                pystray.MenuItem("Restaurar", lambda: self.restore_from_tray(root)),
                pystray.MenuItem("Sair", lambda: self.quit_app_from_tray(root)),
            )
            self.icon = pystray.Icon("HolyricsSync", icon_image, "Holyrics Sync", tray_menu)
            
            # Run tray in a separate thread
            def run_tray():
                self.icon.run()
            
            tray_thread = threading.Thread(target=run_tray, daemon=True)
            tray_thread.start()
            
        except Exception as e:
            print(f"[HolyricsSync] Tray init error: {e}")
            self.icon = None
        
        root.mainloop()
    
    def minimize_to_tray(self, root):
        """Hide window and show tray icon"""
        try:
            root.withdraw()
            if self.icon:
                self.icon.visible = True
                self.icon.stop()
        except Exception:
            pass
    
    def restore_from_tray(self, root):
        """Show window from tray"""
        try:
            root.deiconify()
            root.lift()
            root.focus_force()
            if self.icon:
                self.icon.visible = False
        except Exception:
            pass
    
    def quit_app_from_tray(self, root):
        """Quit app from tray"""
        try:
            if self.icon:
                self.icon.stop()
        except Exception:
            pass
        root.quit()
        root.destroy()
        os._exit(0)

    def show_card(self, icon=None, item=None):
        if not HAS_TK:
            return
        if self.card_window and self.card_window.winfo_exists():
            self.card_window.lift()
            self.card_window.focus_force()
            return

        self.card_window = tk.Toplevel() if hasattr(self, '_root') and self._root else tk.Tk()
        if not hasattr(self, '_root'):
            self._root = self.card_window
            self._root.withdraw()

        win = self.card_window
        win.title("Holyrics Sync")
        win.geometry("340x280")
        win.minsize(320, 260)
        win.configure(bg="#0b1220")
        win.resizable(False, False)
        win.attributes("-topmost", True)

        try:
            win.iconbitmap(default="")
        except Exception:
            pass

        card = tk.Frame(win, bg="#101a30", highlightbackground="#2a3750", highlightthickness=1)
        card.pack(fill="both", expand=True, padx=16, pady=16)

        tk.Label(card, text="Holyrics Sync", bg="#101a30", fg="#eaf0ff",
                 font=("Segoe UI", 14, "bold")).pack(pady=(16, 4))

        pin = self.cfg.get("pin", "—") if self.cfg else "—"
        tk.Label(card, text="PIN de 6 dígitos", bg="#101a30", fg="#8b96b5",
                 font=("Segoe UI", 9)).pack(pady=(8, 0))
        pin_lbl = tk.Label(card, text=pin, bg="#101a30", fg="#22c55e",
                           font=("JetBrains Mono", 22, "bold"))
        pin_lbl.pack(pady=(2, 10))

        separator = tk.Frame(card, bg="#2a3750", height=1)
        separator.pack(fill="x", padx=20, pady=6)

        status_colors = {
            "connected": "#22c55e",
            "pending": "#f5b73d",
            "error": "#f87171",
            "disconnected": "#8b96b5",
        }
        status_icons = {
            "connected": "● Conectado",
            "pending": "◐ Aguardando cadastro",
            "error": "✕ Erro de conexão",
            "disconnected": "○ Desconectado",
        }
        color = status_colors.get(self.status, "#8b96b5")
        status_text = status_icons.get(self.status, "○ Desconectado")

        tk.Label(card, text="Status da conexão", bg="#101a30", fg="#8b96b5",
                 font=("Segoe UI", 9)).pack(pady=(4, 0))
        self.card_status_lbl = tk.Label(card, text=status_text, bg="#101a30", fg=color,
                                         font=("Segoe UI", 11, "bold"))
        self.card_status_lbl.pack(pady=(2, 10))

        if self.status == "connected" and self.worker and self.worker.agent:
            agent = self.worker.agent
            name = agent.get("name", "—")
            group = agent.get("groupName", "—")
            tk.Label(card, text=f"Cliente: {name}", bg="#101a30", fg="#eaf0ff",
                     font=("Segoe UI", 9)).pack()
            if group != "—":
                tk.Label(card, text=f"Grupo: {group}", bg="#101a30", fg="#eaf0ff",
                         font=("Segoe UI", 9)).pack()

        btn_frame = tk.Frame(card, bg="#101a30")
        btn_frame.pack(fill="x", padx=20, pady=(16, 16))

        def copy_pin():
            win.clipboard_clear()
            win.clipboard_append(pin)
            win.update()

        tk.Button(btn_frame, text="Copiar PIN", command=copy_pin,
                  bg="#1a2540", fg="#eaf0ff", font=("Segoe UI", 9),
                  relief="flat", activebackground="#243257", cursor="hand2"
                  ).pack(side="left", expand=True, fill="x", padx=(0, 4))

        tk.Button(btn_frame, text="Fechar", command=win.destroy,
                  bg="#1a2540", fg="#eaf0ff", font=("Segoe UI", 9),
                  relief="flat", activebackground="#243257", cursor="hand2"
                  ).pack(side="left", expand=True, fill="x", padx=(4, 0))

        win.protocol("WM_DELETE_WINDOW", win.destroy)
        win.bind("<Escape>", lambda e: win.destroy())

        self.update_card_status()
        win.mainloop()

    def update_card_status(self):
        if not self.card_window or not self.card_window.winfo_exists():
            return
        status_colors = {
            "connected": "#22c55e",
            "pending": "#f5b73d",
            "error": "#f87171",
            "disconnected": "#8b96b5",
        }
        status_icons = {
            "connected": "● Conectado",
            "pending": "◐ Aguardando cadastro",
            "error": "✕ Erro de conexão",
            "disconnected": "○ Desconectado",
        }
        color = status_colors.get(self.status, "#8b96b5")
        status_text = status_icons.get(self.status, "○ Desconectado")
        if hasattr(self, 'card_status_lbl'):
            self.card_status_lbl.config(text=status_text, fg=color)
        self.card_window.after(2000, self.update_card_status)

    def show_setup(self, icon=None, item=None):
        if not HAS_TK:
            if self.cfg:
                webbrowser.open(self.cfg.get("server", ""))
            return

        if self.setup_window and self.setup_window.winfo_exists():
            self.setup_window.lift()
            self.setup_window.focus_force()
            return

        self.setup_window = tk.Toplevel() if hasattr(self, '_root') and self._root else tk.Tk()
        if not hasattr(self, '_root'):
            self._root = self.setup_window
            self._root.withdraw()

        win = self.setup_window
        win.title("Holyrics Sync - Configuração")
        win.geometry("420x460")
        win.minsize(400, 440)
        win.configure(bg="#0b1220")
        win.resizable(False, False)
        win.attributes("-topmost", True)

        if self.cfg:
            tk.Label(win, text="Holyrics Sync", bg="#0b1220", fg="#eaf0ff",
                     font=("Segoe UI", 16, "bold")).pack(pady=(20, 4))
            tk.Label(win, text="Configurações atuais", bg="#0b1220", fg="#8b96b5",
                     font=("Segoe UI", 10)).pack(pady=(0, 16))
        else:
            tk.Label(win, text="Holyrics Sync", bg="#0b1220", fg="#eaf0ff",
                     font=("Segoe UI", 16, "bold")).pack(pady=(20, 4))
            tk.Label(win, text="Primeira vez: crie uma senha de 6 dígitos\ne informe o link do painel.",
                     bg="#0b1220", fg="#8b96b5", font=("Segoe UI", 10), justify="center").pack(pady=(0, 16))

        def make_entry(parent, label, show=None, default=""):
            frm = tk.Frame(parent, bg="#0b1220")
            tk.Label(frm, text=label, bg="#0b1220", fg="#8b96b5",
                     font=("Segoe UI", 10), anchor="w").pack(fill="x")
            e = tk.Entry(frm, show=show, bg="#101a30", fg="#eaf0ff", insertbackground="#eaf0ff",
                         relief="flat", highlightthickness=1,
                         highlightbackground="#2a3750", highlightcolor="#22c55e")
            e.insert(0, default)
            e.pack(fill="x", ipady=7, pady=(3, 10))
            frm.pack(fill="x", padx=22, pady=4)
            return e

        default_server = self.cfg.get("server", "http://") if self.cfg else "http://"
        default_folder = self.cfg.get("folder", DEFAULT_FOLDER) if self.cfg else DEFAULT_FOLDER
        default_image_folder = self.cfg.get("imageFolder", DEFAULT_IMAGE_FOLDER) if self.cfg else DEFAULT_IMAGE_FOLDER

        e_server = make_entry(win, "Link do painel (ex: http://SEU_IP:5677)", default=default_server)
        e_pin = make_entry(win, "Senha de 6 dígitos" + (" (deixe em branco para manter)" if self.cfg else " (crie uma)"), show="*")
        e_folder = make_entry(win, "Pasta de vídeos do Holyrics", default=default_folder)
        e_image_folder = make_entry(win, "Pasta de imagens do Holyrics", default=default_image_folder)

        def save_setup():
            server = e_server.get().strip().rstrip("/")
            pin = e_pin.get().strip()
            folder = e_folder.get().strip() or DEFAULT_FOLDER
            image_folder = e_image_folder.get().strip() or DEFAULT_IMAGE_FOLDER

            if not server.startswith("http://") and not server.startswith("https://"):
                server = "http://" + server
            if not server.strip("http://").strip("/"):
                messagebox.showwarning("Holyrics Sync", "Informe o link do painel.", parent=win)
                return

            if not self.cfg:
                if not pin.isdigit() or len(pin) != 6:
                    messagebox.showwarning("Holyrics Sync", "A senha deve ter exatamente 6 dígitos.", parent=win)
                    return
            elif pin and (not pin.isdigit() or len(pin) != 6):
                messagebox.showwarning("Holyrics Sync", "A nova senha deve ter 6 dígitos.", parent=win)
                return

            new_cfg = {
                "clientId": self.cfg.get("clientId", str(uuid.uuid4())) if self.cfg else str(uuid.uuid4()),
                "server": server,
                "pinHash": hash_pin(pin) if pin else (self.cfg.get("pinHash") if self.cfg else None),
                "folder": folder,
                "imageFolder": image_folder,
            }
            if pin:
                new_cfg["pin"] = pin

            save_config(new_cfg)
            self.cfg = new_cfg
            win.destroy()
            self.stop_worker()
            self.start_worker()
            if self.icon:
                self.update_tray_tooltip()

        btn_text = "Salvar alterações" if self.cfg else "Salvar e conectar"
        tk.Button(win, text=btn_text, command=save_setup,
                  bg="#22c55e", fg="#06120a", font=("Segoe UI", 11, "bold"),
                  relief="flat", activebackground="#16a34a", cursor="hand2"
                  ).pack(fill="x", padx=22, pady=(14, 4))

        if self.cfg:
            tk.Label(win, text="Se trocar a senha, cadastre novamente no painel (aba Holyrics).",
                     bg="#0b1220", fg="#5b6582", font=("Segoe UI", 9), justify="center").pack(pady=(4, 0))

            def open_panel():
                webbrowser.open(self.cfg.get("server", ""))

            tk.Button(win, text="Abrir painel no navegador", command=open_panel,
                      bg="#1a2540", fg="#eaf0ff", font=("Segoe UI", 9),
                      relief="flat", activebackground="#243257", cursor="hand2"
                      ).pack(fill="x", padx=22, pady=(10, 0))

        win.protocol("WM_DELETE_WINDOW", win.destroy)
        win.bind("<Escape>", lambda e: win.destroy())

    def toggle_startup(self, icon, item):
        exe_path = sys.executable
        if getattr(sys, 'frozen', False):
            exe_path = sys.executable
        else:
            exe_path = os.path.abspath(sys.argv[0])

        if is_in_startup():
            remove_from_startup()
        else:
            add_to_startup(exe_path)
        if self.icon:
            self.icon.update_menu()

    def quit_app(self, icon=None, item=None):
        self.stop_worker()
        if self.icon:
            self.icon.stop()
        if hasattr(self, '_root') and self._root:
            try:
                self._root.quit()
            except Exception:
                pass
        os._exit(0)


def main():
    if "--minimized" in sys.argv or "-m" in sys.argv:
        pass

    if not HAS_TRAY:
        print("AVISO: pystray não instalado. Instale com: pip install pystray pillow")
        print("O app rodará sem ícone na bandeja.")

    if show_setup:
        app = TrayApp()
        app.show_setup()
        return

    app = TrayApp()
    app.run()


if __name__ == "__main__":
    main()