"""GUI launcher do IronDome Cloud Operator.

Tkinter:
  - Primeira execução: dialog pedindo api_key
  - Janela de status com log + botão sair

Usa o motor em irondome_operator.py (importado).
"""
from __future__ import annotations
import json
import os
import sys
import threading
import time
import tkinter as tk
from tkinter import scrolledtext, ttk, messagebox
from pathlib import Path

# resolve path mesmo dentro de PyInstaller bundle
if getattr(sys, 'frozen', False):
    BASE = Path(sys._MEIPASS) if hasattr(sys, '_MEIPASS') else Path(sys.executable).parent
else:
    BASE = Path(__file__).parent

sys.path.insert(0, str(BASE))
import irondome_operator as core

VERSION = core.VERSION
CONFIG_PATH = core.CONFIG_PATH
CONFIG_DIR = core.CONFIG_DIR

VERSION_URL = 'https://irondome-site.vercel.app/operator-version.json'


def fetch_latest_version() -> dict | None:
    """Consulta a versão mais recente do site. Retorna None em erro."""
    import urllib.request
    try:
        with urllib.request.urlopen(VERSION_URL, timeout=10) as r:
            return json.loads(r.read().decode())
    except Exception:
        return None


def version_tuple(v: str) -> tuple:
    try:
        return tuple(int(x) for x in v.split('.'))
    except Exception:
        return (0, 0, 0)


# ─── First-run dialog ──────────────────────────────────────────────────────
def first_run_dialog(parent=None) -> dict | None:
    """Abre janela modal pedindo api_key. Retorna dict com config ou None se cancelado."""
    win = tk.Toplevel(parent) if parent else tk.Tk()
    win.title('IronDome Operator — Configuração inicial')
    win.geometry('480x300')
    win.resizable(False, False)
    win.configure(bg='#070a12')

    result = {'cfg': None}

    style = ttk.Style()
    style.theme_use('clam')
    style.configure('Dark.TFrame', background='#070a12')
    style.configure('Dark.TLabel', background='#070a12', foreground='#e6eefc', font=('SF Pro Text', 12))
    style.configure('Muted.TLabel', background='#070a12', foreground='#8aa0b8', font=('SF Pro Text', 11))
    style.configure('Dark.TButton', background='#9333ea', foreground='#ffffff', borderwidth=0, font=('SF Pro Text', 12, 'bold'))
    style.map('Dark.TButton', background=[('active', '#7e22ce')])
    style.configure('Dark.TEntry', fieldbackground='#0a0e17', foreground='#e6eefc', borderwidth=1, insertcolor='#e6eefc')

    frm = ttk.Frame(win, style='Dark.TFrame', padding=24)
    frm.pack(fill='both', expand=True)

    ttk.Label(frm, text='⚙️  Configuração inicial', style='Dark.TLabel', font=('SF Pro Display', 18, 'bold')).pack(anchor='w')
    ttk.Label(frm, text='Cole sua api_key da cloud pra começar.\nVocê acha em irondome-cloud.vercel.app → Admin.',
              style='Muted.TLabel', justify='left').pack(anchor='w', pady=(6, 14))

    ttk.Label(frm, text='api_key', style='Muted.TLabel').pack(anchor='w', pady=(0, 4))
    key_var = tk.StringVar()
    entry = ttk.Entry(frm, textvariable=key_var, style='Dark.TEntry', width=50, font=('JetBrains Mono', 11))
    entry.pack(fill='x', pady=(0, 10), ipady=6)
    entry.focus()

    ttk.Label(frm, text='URL da cloud', style='Muted.TLabel').pack(anchor='w', pady=(0, 4))
    url_var = tk.StringVar(value=core.DEFAULT_CLOUD_URL)
    ttk.Entry(frm, textvariable=url_var, style='Dark.TEntry', width=50, font=('JetBrains Mono', 11)).pack(fill='x', pady=(0, 16), ipady=6)

    err_var = tk.StringVar()
    ttk.Label(frm, textvariable=err_var, style='Muted.TLabel', foreground='#fca5a5').pack(anchor='w')

    def on_save():
        k = key_var.get().strip()
        if not k or len(k) < 20:
            err_var.set('api_key parece inválida (muito curta)')
            return
        cfg = {'api_key': k, 'cloud_url': url_var.get().strip() or core.DEFAULT_CLOUD_URL}
        # tenta validar + identificar nome do cliente
        try:
            core.http_json('GET', f"{cfg['cloud_url']}/api/v1/operator/state", k)
            me = core.http_json('GET', f"{cfg['cloud_url']}/api/v1/me", k)
        except Exception as e:
            err_var.set(f'falha ao conectar: {e}')
            return
        cfg['client_name'] = me.get('name', '?')
        cfg['client_id'] = me.get('id')
        core.save_config(cfg)
        # confirma com o nome
        messagebox.showinfo(
            'Cliente identificado',
            f"Conectado como:\n\n{me.get('name', '?')}\n(id {me.get('id')})\n\n"
            f"Tudo certo. Vamos iniciar.",
        )
        result['cfg'] = cfg
        win.destroy()

    btns = ttk.Frame(frm, style='Dark.TFrame')
    btns.pack(fill='x', pady=(8, 0))
    ttk.Button(btns, text='Salvar e iniciar', style='Dark.TButton', command=on_save).pack(side='right', ipady=6, ipadx=12)

    win.bind('<Return>', lambda _: on_save())
    win.protocol('WM_DELETE_WINDOW', win.destroy)
    win.wait_window()
    return result['cfg']


# ─── Status window ─────────────────────────────────────────────────────────
class StatusWindow:
    def __init__(self, cfg: dict):
        self.cfg = cfg
        self.root = tk.Tk()
        self.root.title('IronDome Cloud Operator')
        self.root.geometry('640x420')
        self.root.configure(bg='#070a12')

        # header
        hdr = tk.Frame(self.root, bg='#070a12')
        hdr.pack(fill='x', padx=16, pady=(12, 4))
        tk.Label(hdr, text='🤖  IronDome Operator', bg='#070a12', fg='#e6eefc',
                 font=('SF Pro Display', 16, 'bold')).pack(side='left')
        self.status_var = tk.StringVar(value='conectando…')
        self.status_label = tk.Label(hdr, textvariable=self.status_var, bg='#070a12', fg='#94a3b8',
                                     font=('SF Pro Text', 11, 'bold'))
        self.status_label.pack(side='right')

        # client name bar
        cli = tk.Frame(self.root, bg='#070a12')
        cli.pack(fill='x', padx=16, pady=(0, 4))
        client_name = cfg.get('client_name', '?')
        client_id = cfg.get('client_id', '?')
        tk.Label(cli, text='Cliente:', bg='#070a12', fg='#8aa0b8',
                 font=('SF Pro Text', 11)).pack(side='left')
        self.client_label = tk.Label(cli, text=f' {client_name}',
                                     bg='#070a12', fg='#c084fc',
                                     font=('SF Pro Text', 12, 'bold'))
        self.client_label.pack(side='left')
        tk.Label(cli, text=f'  · id {client_id}', bg='#070a12', fg='#475569',
                 font=('JetBrains Mono', 10)).pack(side='left')

        # info bar
        info = tk.Frame(self.root, bg='#070a12')
        info.pack(fill='x', padx=16, pady=(0, 8))
        self.info_var = tk.StringVar(value='—')
        tk.Label(info, textvariable=self.info_var, bg='#070a12', fg='#8aa0b8',
                 font=('JetBrains Mono', 10)).pack(side='left')

        # log
        self.log = scrolledtext.ScrolledText(
            self.root, bg='#0a0e17', fg='#cbd5e1', insertbackground='#e6eefc',
            font=('JetBrains Mono', 10), borderwidth=0, height=18,
        )
        self.log.pack(fill='both', expand=True, padx=16, pady=4)
        self.log.configure(state='disabled')

        # footer
        ft = tk.Frame(self.root, bg='#070a12')
        ft.pack(fill='x', padx=16, pady=10)
        tk.Label(ft, text=f'v{VERSION}', bg='#070a12', fg='#475569',
                 font=('SF Pro Text', 10)).pack(side='left')
        tk.Button(ft, text='Sair', bg='#1f2937', fg='#e6eefc', borderwidth=0,
                  font=('SF Pro Text', 11), padx=14, pady=4,
                  command=self.quit_app).pack(side='right')
        tk.Button(ft, text='🔑 Trocar key', bg='#1f2937', fg='#e6eefc', borderwidth=0,
                  font=('SF Pro Text', 11), padx=14, pady=4,
                  command=self.change_key).pack(side='right', padx=(0, 8))

        self.stop_flag = threading.Event()
        self.root.protocol('WM_DELETE_WINDOW', self.quit_app)

        # menu bar
        menubar = tk.Menu(self.root)
        app_menu = tk.Menu(menubar, tearoff=0)
        app_menu.add_command(label='Sobre o IronDome Operator', command=self.show_about)
        app_menu.add_command(label='Buscar atualizações…', command=self.check_for_updates)
        app_menu.add_separator()
        app_menu.add_command(label='🔑 Trocar key', command=self.change_key)
        app_menu.add_separator()
        app_menu.add_command(label='Sair', command=self.quit_app)
        menubar.add_cascade(label='IronDome', menu=app_menu)
        self.root.config(menu=menubar)

        # patch print → log
        self._orig_print = print
        import builtins
        builtins.print = self._gui_print

        # start backend thread
        threading.Thread(target=self._run_backend, daemon=True).start()
        # update status periodically
        self.root.after(2000, self._update_status_pill)
        # check de atualização leve no boot (3s depois pra não atrapalhar inicialização)
        self.root.after(3000, self._silent_update_check)

    def _gui_print(self, *args, **kwargs):
        msg = ' '.join(str(a) for a in args)
        self.root.after(0, self._append_log, msg)
        self._orig_print(*args, **kwargs)

    def _append_log(self, msg: str):
        self.log.configure(state='normal')
        self.log.insert('end', msg + '\n')
        # cap at 500 lines
        n_lines = int(self.log.index('end-1c').split('.')[0])
        if n_lines > 500:
            self.log.delete('1.0', f'{n_lines - 500}.0')
        self.log.see('end')
        self.log.configure(state='disabled')

    def _update_status_pill(self):
        try:
            st = core.http_json('GET', f"{self.cfg['cloud_url']}/api/v1/operator/state", self.cfg['api_key'])
            if st.get('paused_reason'):
                self.status_var.set('● PAUSADO')
                self.status_label.config(fg='#fca5a5')
            elif st.get('enabled'):
                self.status_var.set('● OPERANDO')
                self.status_label.config(fg='#c084fc')
            else:
                self.status_var.set('● conectado')
                self.status_label.config(fg='#86efac')
            self.info_var.set(
                f"{st.get('strategy', '?')} · unit R${st.get('unit_brl', '?')} · cap R${st.get('daily_cap_brl', '?')} · {st.get('house', '?')}"
            )
        except Exception:
            self.status_var.set('● offline')
            self.status_label.config(fg='#94a3b8')
        self.root.after(3000, self._update_status_pill)

    def _run_backend(self):
        try:
            core.run_main_loop(self.cfg, self.stop_flag)
        except Exception as e:
            self._append_log(f'[ERRO BACKEND] {e}')

    def show_about(self):
        """Diálogo Sobre — versão atual + botão buscar atualizações."""
        win = tk.Toplevel(self.root)
        win.title('Sobre o IronDome Operator')
        win.geometry('420x280')
        win.resizable(False, False)
        win.configure(bg='#070a12')
        win.transient(self.root)

        frm = tk.Frame(win, bg='#070a12', padx=24, pady=24)
        frm.pack(fill='both', expand=True)

        tk.Label(frm, text='🤖 IronDome Operator',
                 bg='#070a12', fg='#e6eefc',
                 font=('SF Pro Display', 18, 'bold')).pack(anchor='w')
        tk.Label(frm, text='Auto-click local pra estratégias da cloud',
                 bg='#070a12', fg='#8aa0b8',
                 font=('SF Pro Text', 11)).pack(anchor='w', pady=(2, 14))

        info = tk.Frame(frm, bg='#0a0e17', highlightbackground='#1f2937', highlightthickness=1)
        info.pack(fill='x', pady=(0, 14))
        tk.Label(info, text=f'Versão atual: ', bg='#0a0e17', fg='#8aa0b8',
                 font=('SF Pro Text', 12)).pack(side='left', padx=(12, 0), pady=10)
        tk.Label(info, text=f'v{VERSION}', bg='#0a0e17', fg='#c084fc',
                 font=('JetBrains Mono', 13, 'bold')).pack(side='left', pady=10)

        cli = self.cfg.get('client_name', '?')
        tk.Label(frm, text=f'Cliente: {cli}', bg='#070a12', fg='#8aa0b8',
                 font=('SF Pro Text', 11)).pack(anchor='w', pady=(0, 4))
        tk.Label(frm, text='Cloud: ' + self.cfg.get('cloud_url', core.DEFAULT_CLOUD_URL),
                 bg='#070a12', fg='#8aa0b8',
                 font=('SF Pro Text', 10)).pack(anchor='w', pady=(0, 14))

        btns = tk.Frame(frm, bg='#070a12')
        btns.pack(fill='x')
        tk.Button(btns, text='🔄 Buscar atualizações',
                  bg='#9333ea', fg='#ffffff', borderwidth=0,
                  font=('SF Pro Text', 11, 'bold'), padx=14, pady=6,
                  command=lambda: (win.destroy(), self.check_for_updates())).pack(side='left')
        tk.Button(btns, text='Fechar',
                  bg='#1f2937', fg='#e6eefc', borderwidth=0,
                  font=('SF Pro Text', 11), padx=14, pady=6,
                  command=win.destroy).pack(side='right')

    def check_for_updates(self):
        """Consulta o site e mostra resultado."""
        info = fetch_latest_version()
        if not info:
            messagebox.showwarning('Sem conexão',
                'Não consegui consultar o servidor de versões.\nVerifique sua conexão.',
                parent=self.root)
            return
        latest = info.get('latest', '0.0.0')
        if version_tuple(latest) <= version_tuple(VERSION):
            messagebox.showinfo('Atualizado',
                f'Você está na versão mais recente.\n\nv{VERSION}',
                parent=self.root)
            return
        # Tem atualização — pergunta
        notes = info.get('release_notes', '(sem notas)')
        ok = messagebox.askyesno(
            'Atualização disponível',
            f'Nova versão disponível: v{latest}\n'
            f'Você está em v{VERSION}\n\n'
            f'O que mudou:\n{notes}\n\n'
            f'Quer abrir a página de download agora?\n'
            f'(O app vai fechar pra você instalar manualmente.)',
            parent=self.root,
        )
        if not ok:
            return
        # Abre o DMG no navegador padrão (download)
        import webbrowser
        webbrowser.open(info.get('dmg_url') or VERSION_URL)
        messagebox.showinfo('Download iniciado',
            'O download começou no seu navegador.\n\n'
            'Quando terminar:\n'
            '1. Abra o .dmg na pasta Downloads\n'
            '2. Arraste o app por cima do existente em /Applications\n'
            '3. Abra o app de novo\n\n'
            'O IronDome Operator vai fechar agora.',
            parent=self.root)
        self.quit_app()

    def _silent_update_check(self):
        """Roda no boot. Se tem update, pisca um aviso suave no log."""
        info = fetch_latest_version()
        if info and version_tuple(info.get('latest', '0.0.0')) > version_tuple(VERSION):
            self._append_log(f'ℹ️  Nova versão disponível: v{info["latest"]} (você está em v{VERSION}). Menu IronDome → Buscar atualizações.')

    def change_key(self):
        """Abre dialog pedindo nova api_key. Se válida, salva e reinicia o app."""
        if not messagebox.askyesno(
            'Trocar API key',
            'Vai abrir o assistente de configuração pra inserir uma key nova.\n\n'
            'O operator vai parar de operar enquanto você confirma a nova key. '
            'Depois de salvar, o app fecha e você abre de novo (uma vez só).\n\n'
            'Continuar?',
            parent=self.root,
        ):
            return
        # apaga config atual
        try:
            if CONFIG_PATH.exists():
                CONFIG_PATH.unlink()
        except Exception as e:
            messagebox.showerror('Erro', f'Não consegui apagar config:\n{e}', parent=self.root)
            return
        # abre dialog modal pra capturar key nova
        new_cfg = first_run_dialog(parent=self.root)
        if not new_cfg:
            messagebox.showwarning(
                'Cancelado',
                'Você cancelou. Próxima vez que abrir, vai pedir a key.',
                parent=self.root,
            )
            self.quit_app()
            return
        messagebox.showinfo(
            'Key trocada',
            'Nova key salva.\n\nO app vai fechar agora. Abra de novo pra começar a operar com a key nova.',
            parent=self.root,
        )
        self.quit_app()

    def quit_app(self):
        self.stop_flag.set()
        self.root.after(200, self.root.destroy)
        # garante que processo termina
        self.root.after(500, lambda: os._exit(0))

    def run(self):
        self.root.mainloop()


# ─── Entry point ───────────────────────────────────────────────────────────
def main():
    cfg = core.load_config()
    if not cfg.get('api_key'):
        cfg = first_run_dialog()
        if not cfg:
            sys.exit(0)
    # Backfill: configs antigos não têm client_name. Busca via /me.
    if not cfg.get('client_name'):
        try:
            me = core.http_json('GET', f"{cfg.get('cloud_url', core.DEFAULT_CLOUD_URL)}/api/v1/me", cfg['api_key'])
            cfg['client_name'] = me.get('name', '?')
            cfg['client_id'] = me.get('id')
            core.save_config(cfg)
        except Exception:
            cfg.setdefault('client_name', '?')
            cfg.setdefault('client_id', '?')
    sw = StatusWindow(cfg)
    sw.run()


if __name__ == '__main__':
    main()
