import os
import queue
import threading
import tkinter as tk
from datetime import datetime
from tkinter import messagebox, ttk

from config import (DEFAULT_INSTRUMENTS, load_config, load_sent, save_config,
                    save_sent)
from cookie_reader import find_cookies
from dtc_client import DTCClient, fills_to_trades, monitor_realtime
from tralgo_api import build_session, send_trade

try:
    from PIL import Image, ImageTk
    _PIL_AVAILABLE = True
except ImportError:
    _PIL_AVAILABLE = False

_ICON_PATH      = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
_ICON_FULL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon_full.png")


def _load_icon(path, size):
    """Open icon, crop transparent padding, add 8 % margin, resize to size×size."""
    img = Image.open(path).convert("RGBA")
    bbox = img.getbbox()
    if bbox:
        img = img.crop(bbox)
    m = max(img.width, img.height) // 12   # ~8 % margin
    canvas_sz = max(img.width, img.height) + 2 * m
    canvas = Image.new("RGBA", (canvas_sz, canvas_sz), (0, 0, 0, 0))
    ox = (canvas_sz - img.width)  // 2
    oy = (canvas_sz - img.height) // 2
    canvas.paste(img, (ox, oy), img)
    return canvas.resize((size, size), Image.LANCZOS)


# ── Config window ─────────────────────────────────────────────────────────────

class ConfigWindow:
    _PRESETS = list(DEFAULT_INSTRUMENTS.keys())  # MNQ, NQ, ES, MES

    def __init__(self, parent, config, on_save):
        self.win = tk.Toplevel(parent)
        self.win.title("Einstellungen – GizerBridge")
        self.win.resizable(False, False)
        self.win.grab_set()
        self._config = config
        self._on_save = on_save
        self._inst_vars = {}
        self._inst_frame = None
        self._build()
        self.win.protocol("WM_DELETE_WINDOW", self.win.destroy)

    def _build(self):
        cfg = self._config
        pad = {"padx": 10, "pady": 3}

        self._api_key_var = self._labeled_entry("API Key *", cfg.get("api_key", ""), **pad)
        self._email_var   = self._labeled_entry("Tralgo E-Mail", cfg.get("email", ""), **pad)
        self._dtc_host_var = self._labeled_entry(
            "SC DTC Host", cfg.get("dtc_host", "localhost"), **pad)

        row = tk.Frame(self.win)
        row.pack(fill=tk.X, **pad)
        tk.Label(row, text="SC DTC Port", width=20, anchor="w").pack(side=tk.LEFT)
        self._dtc_port_var = tk.StringVar(value=str(cfg.get("dtc_port", 11099)))
        tk.Entry(row, textvariable=self._dtc_port_var, width=8).pack(side=tk.LEFT)
        tk.Label(row, text="(Sierra Chart → DTC Protocol Server aktivieren)",
                 fg="gray", font=("", 8)).pack(side=tk.LEFT, padx=(8, 0))

        tk.Label(self.win, text="Konten:", anchor="w",
                 font=("", 9, "bold")).pack(fill=tk.X, padx=10, pady=(10, 2))
        acc_outer = tk.Frame(self.win)
        acc_outer.pack(fill=tk.X, padx=10)
        self._acc_listbox = tk.Listbox(acc_outer, height=4, width=48, selectmode=tk.SINGLE)
        self._acc_listbox.pack(side=tk.LEFT)
        for acc in cfg.get("accounts", []):
            self._acc_listbox.insert(tk.END, acc)

        add_row = tk.Frame(self.win)
        add_row.pack(fill=tk.X, padx=10, pady=2)
        self._acc_entry = tk.Entry(add_row, width=35)
        self._acc_entry.pack(side=tk.LEFT)
        self._acc_entry.bind("<Return>", lambda _: self._add_account())
        tk.Button(add_row, text="Hinzufügen", command=self._add_account).pack(side=tk.LEFT, padx=4)
        tk.Button(add_row, text="Entfernen",  command=self._remove_account).pack(side=tk.LEFT)

        tk.Label(self.win, text="Instrumente:", anchor="w",
                 font=("", 9, "bold")).pack(fill=tk.X, padx=10, pady=(10, 2))
        outer = tk.LabelFrame(self.win, text="")
        outer.pack(fill=tk.X, padx=10, pady=2)

        hdr = tk.Frame(outer)
        hdr.pack(fill=tk.X, padx=4, pady=(4, 0))
        tk.Label(hdr, text="",           width=3).pack(side=tk.LEFT)
        tk.Label(hdr, text="Symbol",     width=10, anchor="w").pack(side=tk.LEFT)
        tk.Label(hdr, text="Tick-Größe", width=11, anchor="w").pack(side=tk.LEFT)
        tk.Label(hdr, text="Tick-Wert $",width=11, anchor="w").pack(side=tk.LEFT)

        self._inst_frame = tk.Frame(outer)
        self._inst_frame.pack(fill=tk.X)

        existing = cfg.get("instruments", {})
        rendered = set()
        for name in self._PRESETS:
            self._add_inst_row(name, existing.get(name, DEFAULT_INSTRUMENTS.get(name, {})))
            rendered.add(name)
        for name, data in existing.items():
            if name not in rendered:
                self._add_inst_row(name, data)

        custom = tk.Frame(outer)
        custom.pack(fill=tk.X, padx=4, pady=6)
        tk.Label(custom, text="Weiteres:").pack(side=tk.LEFT)
        self._custom_name = tk.Entry(custom, width=7)
        self._custom_name.pack(side=tk.LEFT, padx=(3, 6))
        tk.Label(custom, text="Tick-Größe:").pack(side=tk.LEFT)
        self._custom_tick_size = tk.Entry(custom, width=6)
        self._custom_tick_size.insert(0, "0.25")
        self._custom_tick_size.pack(side=tk.LEFT, padx=(2, 6))
        tk.Label(custom, text="Tick-Wert $:").pack(side=tk.LEFT)
        self._custom_tick_value = tk.Entry(custom, width=6)
        self._custom_tick_value.pack(side=tk.LEFT, padx=2)
        tk.Button(custom, text="+", width=3, command=self._add_custom_inst).pack(side=tk.LEFT, padx=4)

        btn = tk.Frame(self.win)
        btn.pack(pady=10)
        tk.Button(btn, text="Speichern", width=12, command=self._save).pack(side=tk.LEFT, padx=8)
        tk.Button(btn, text="Abbrechen", width=12, command=self.win.destroy).pack(side=tk.LEFT)

    def _labeled_entry(self, label, value, **pack_kwargs):
        row = tk.Frame(self.win)
        row.pack(fill=tk.X, **pack_kwargs)
        tk.Label(row, text=label, width=20, anchor="w").pack(side=tk.LEFT)
        var = tk.StringVar(value=value)
        tk.Entry(row, textvariable=var, width=40).pack(side=tk.LEFT)
        return var

    def _add_inst_row(self, name, data):
        if name in self._inst_vars:
            return
        row = tk.Frame(self._inst_frame)
        row.pack(fill=tk.X, padx=4, pady=1)
        preset_defaults = DEFAULT_INSTRUMENTS.get(name, {})
        enabled_var    = tk.BooleanVar(value=data.get("enabled", name in self._PRESETS))
        tick_size_var  = tk.StringVar(value=str(data.get("tick_size",  preset_defaults.get("tick_size",  0.25))))
        tick_value_var = tk.StringVar(value=str(data.get("tick_value", preset_defaults.get("tick_value", 0.0))))
        tk.Checkbutton(row, variable=enabled_var).pack(side=tk.LEFT)
        tk.Label(row, text=name, width=10, anchor="w").pack(side=tk.LEFT)
        tk.Entry(row, textvariable=tick_size_var,  width=9).pack(side=tk.LEFT, padx=2)
        tk.Entry(row, textvariable=tick_value_var, width=9).pack(side=tk.LEFT, padx=2)
        self._inst_vars[name] = {
            "enabled":    enabled_var,
            "tick_size":  tick_size_var,
            "tick_value": tick_value_var,
        }

    def _add_custom_inst(self):
        name = self._custom_name.get().strip().upper()
        if not name:
            return
        if name in self._inst_vars:
            messagebox.showinfo("Hinweis", f"{name} ist bereits vorhanden.", parent=self.win)
            return
        try:
            ts = float(self._custom_tick_size.get())
            tv = float(self._custom_tick_value.get())
        except ValueError:
            messagebox.showerror("Fehler", "Ungültige Zahlenwerte.", parent=self.win)
            return
        self._add_inst_row(name, {"enabled": True, "tick_size": ts, "tick_value": tv})
        self._custom_name.delete(0, tk.END)
        self._custom_tick_value.delete(0, tk.END)

    def _add_account(self):
        name = self._acc_entry.get().strip()
        if name and name not in self._acc_listbox.get(0, tk.END):
            self._acc_listbox.insert(tk.END, name)
        self._acc_entry.delete(0, tk.END)

    def _remove_account(self):
        sel = self._acc_listbox.curselection()
        if sel:
            self._acc_listbox.delete(sel[0])

    def _save(self):
        api_key = self._api_key_var.get().strip()
        if not api_key:
            messagebox.showerror("Fehler", "API Key ist ein Pflichtfeld.", parent=self.win)
            return
        try:
            dtc_port = int(self._dtc_port_var.get().strip())
        except ValueError:
            messagebox.showerror("Fehler", "DTC Port muss eine Zahl sein.", parent=self.win)
            return
        instruments = {}
        for name, v in self._inst_vars.items():
            try:
                ts = float(v["tick_size"].get())
                tv = float(v["tick_value"].get())
            except ValueError:
                messagebox.showerror("Fehler", f"Ungültige Tick-Werte für {name}.", parent=self.win)
                return
            instruments[name] = {
                "enabled":    v["enabled"].get(),
                "tick_size":  ts,
                "tick_value": tv,
            }
        self._on_save({
            "api_key":     api_key,
            "email":       self._email_var.get().strip(),
            "dtc_host":    self._dtc_host_var.get().strip() or "localhost",
            "dtc_port":    dtc_port,
            "accounts":    list(self._acc_listbox.get(0, tk.END)),
            "instruments": instruments,
        })
        self.win.destroy()


# ── Manual import window ──────────────────────────────────────────────────────

class ManualImportWindow:
    _CHK_ON  = "☑"
    _CHK_OFF = "☐"

    _COLS  = ("chk", "date", "account", "instrument", "direction",
              "entry", "exit", "pnl", "ticks", "status")
    _HDRS  = ("",    "Datum", "Konto", "Symbol", "Richtung",
              "Entry", "Exit", "PnL $", "Ticks", "Status")
    _WIDTHS = (30, 100, 140, 65, 70, 80, 80, 70, 55, 70)

    def __init__(self, parent, config, sent, session, api_key, on_imported):
        self.win = tk.Toplevel(parent)
        self.win.title("Manueller Import – GizerBridge")
        self.win.geometry("870x520")
        self.win.resizable(True, True)
        self._config      = config
        self._sent        = sent        # set of trade IDs already sent
        self._session     = session     # requests.Session or None
        self._api_key     = api_key
        self._on_imported = on_imported # callback(trade_id) → updates App.sent
        self._rows        = []          # [(iid, trade_dict, is_sent)]
        self._build()

    def _build(self):
        # ── Filter bar ────────────────────────────────────────────────────────
        ctrl = tk.Frame(self.win)
        ctrl.pack(fill=tk.X, padx=10, pady=(10, 4))

        tk.Label(ctrl, text="Konto:").pack(side=tk.LEFT)
        accounts = ["Alle"] + list(self._config.get("accounts", []))
        self._account_var = tk.StringVar(value=accounts[0] if accounts else "")
        ttk.Combobox(ctrl, textvariable=self._account_var, values=accounts,
                     width=22, state="readonly").pack(side=tk.LEFT, padx=(4, 14))

        tk.Label(ctrl, text="Von:").pack(side=tk.LEFT)
        self._date_from_var = tk.StringVar(value="2026-01-01")
        tk.Entry(ctrl, textvariable=self._date_from_var, width=11).pack(side=tk.LEFT, padx=(4, 14))

        tk.Label(ctrl, text="Bis:").pack(side=tk.LEFT)
        self._date_to_var = tk.StringVar(value=datetime.now().strftime("%Y-%m-%d"))
        tk.Entry(ctrl, textvariable=self._date_to_var, width=11).pack(side=tk.LEFT, padx=(4, 14))

        self._search_btn = tk.Button(ctrl, text="Suchen", command=self._search)
        self._search_btn.pack(side=tk.LEFT)

        self._count_lbl = tk.Label(ctrl, text="", fg="gray")
        self._count_lbl.pack(side=tk.LEFT, padx=10)

        tk.Label(self.win,
                 text="ℹ Timestamps direkt aus SC via DTC — echte Füll-Zeiten.",
                 fg="#888888", font=("", 8), anchor="w",
                 ).pack(fill=tk.X, padx=11, pady=(0, 2))

        # ── Treeview ──────────────────────────────────────────────────────────
        tree_outer = tk.Frame(self.win)
        tree_outer.pack(fill=tk.BOTH, expand=True, padx=10, pady=4)

        vsb = ttk.Scrollbar(tree_outer, orient="vertical")
        hsb = ttk.Scrollbar(tree_outer, orient="horizontal")
        vsb.pack(side=tk.RIGHT,  fill=tk.Y)
        hsb.pack(side=tk.BOTTOM, fill=tk.X)

        self._tree = ttk.Treeview(
            tree_outer, columns=self._COLS, show="headings",
            yscrollcommand=vsb.set, xscrollcommand=hsb.set,
            selectmode="none",
        )
        vsb.config(command=self._tree.yview)
        hsb.config(command=self._tree.xview)

        for col, hdr, w in zip(self._COLS, self._HDRS, self._WIDTHS):
            anchor = "e" if col in ("entry", "exit", "pnl", "ticks") else "w"
            self._tree.heading(col, text=hdr, anchor=anchor)
            self._tree.column(col, width=w, minwidth=w,
                              stretch=(col == "account"), anchor=anchor)

        self._tree.tag_configure("sent",  foreground="#aaaaaa")
        self._tree.tag_configure("sel",   foreground="#000000", background="#e8f4e8")
        self._tree.tag_configure("desel", foreground="#333333")
        self._tree.tag_configure("ok",    foreground="darkgreen", background="#e8f4e8")
        self._tree.tag_configure("err",   foreground="#cc0000")

        self._tree.bind("<ButtonRelease-1>", self._on_click)
        self._tree.pack(fill=tk.BOTH, expand=True)

        # ── Bottom bar ────────────────────────────────────────────────────────
        bot = tk.Frame(self.win)
        bot.pack(fill=tk.X, padx=10, pady=(4, 10))

        tk.Button(bot, text="Alle auswählen", command=self._select_all).pack(side=tk.LEFT, padx=(0, 4))
        tk.Button(bot, text="Keine",          command=self._select_none).pack(side=tk.LEFT, padx=4)

        self._status_lbl = tk.Label(bot, text="", fg="gray")
        self._status_lbl.pack(side=tk.LEFT, padx=12)

        self._import_btn = tk.Button(
            bot, text="Ausgewählte importieren",
            command=self._import_selected, state=tk.DISABLED,
        )
        self._import_btn.pack(side=tk.RIGHT)

    # ── Search ────────────────────────────────────────────────────────────────

    def _search(self):
        self._tree.delete(*self._tree.get_children())
        self._rows.clear()
        self._import_btn.config(state=tk.DISABLED)
        self._count_lbl.config(text="Verbinde mit SC DTC…")
        self._status_lbl.config(text="")
        self._search_btn.config(state=tk.DISABLED)
        self.win.update_idletasks()

        account_sel  = self._account_var.get()
        date_from    = self._date_from_var.get().strip()
        date_to      = self._date_to_var.get().strip()
        cfg_accounts = self._config.get("accounts", [])
        accounts     = cfg_accounts if account_sel == "Alle" else [account_sel]
        instruments  = {k: v for k, v in self._config.get("instruments", {}).items()
                        if v.get("enabled", True)}
        host = self._config.get("dtc_host", "localhost")
        port = self._config.get("dtc_port", 11099)

        def _run():
            all_fills = []
            dtc_error = None

            try:
                client = DTCClient(host, port)
                client.connect()
                client.logon()
                for account in accounts:
                    fills = client.request_fills(account, num_days=90)
                    all_fills.extend(fills)
                client.disconnect()
            except Exception as e:
                dtc_error = str(e)

            if dtc_error and not all_fills:
                self.win.after(0, lambda: self._search_done([], dtc_error))
                return

            filtered_fills = []
            for f in all_fills:
                if not f.get("datetime"):
                    filtered_fills.append(f)
                    continue
                d = f["datetime"].strftime("%Y-%m-%d")
                if date_from and d < date_from:
                    continue
                if date_to and d > date_to:
                    continue
                filtered_fills.append(f)

            all_trades = fills_to_trades(filtered_fills, instruments)
            err = dtc_error if not all_trades else None
            self.win.after(0, lambda: self._search_done(all_trades, err))

        threading.Thread(target=_run, daemon=True).start()

    def _search_done(self, trades, error_msg):
        self._search_btn.config(state=tk.NORMAL)
        if error_msg:
            self._count_lbl.config(text=f"Verbindungsfehler: {error_msg[:60]}")
            return

        total = 0
        for trade in trades:
            is_sent = trade["trade_id"] in self._sent
            tag     = "sent" if is_sent else "desel"
            pnl_fmt = f"{trade['pnl']:+.2f}" if trade["pnl"] != 0 else "0.00"
            date_str = trade["entry_datetime"][:10] if trade["entry_datetime"] else ""
            iid = self._tree.insert(
                "", tk.END,
                values=(
                    self._CHK_OFF,
                    date_str,
                    trade["account"],
                    trade["instrument"],
                    trade["direction"],
                    trade["entry_price"],
                    trade["exit_price"],
                    pnl_fmt,
                    trade["pnl_ticks"],
                    "bereits importiert" if is_sent else "",
                ),
                tags=(tag,),
            )
            self._rows.append((iid, trade, is_sent))
            total += 1

        already = sum(1 for _, _, s in self._rows if s)
        parts = [f"{total} Trade(s) gefunden"]
        if already:
            parts.append(f"{already} bereits importiert")
        self._count_lbl.config(text=" – ".join(parts))

    # ── Checkbox interaction ──────────────────────────────────────────────────

    def _on_click(self, event):
        iid = self._tree.identify_row(event.y)
        if not iid or self._tree.identify_region(event.x, event.y) != "cell":
            return
        for row_iid, trade, is_sent in self._rows:
            if row_iid != iid:
                continue
            vals = list(self._tree.item(iid, "values"))
            if vals[0] == self._CHK_OFF:
                vals[0] = self._CHK_ON
                self._tree.item(iid, values=vals, tags=("sel",))
            else:
                vals[0] = self._CHK_OFF
                self._tree.item(iid, values=vals, tags=("sent" if is_sent else "desel",))
            break
        self._refresh_import_btn()

    def _select_all(self):
        for iid, _, is_sent in self._rows:
            vals = list(self._tree.item(iid, "values"))
            vals[0] = self._CHK_ON
            self._tree.item(iid, values=vals, tags=("sel",))
        self._refresh_import_btn()

    def _select_none(self):
        for iid, _, is_sent in self._rows:
            vals = list(self._tree.item(iid, "values"))
            vals[0] = self._CHK_OFF
            self._tree.item(iid, values=vals, tags=("sent" if is_sent else "desel",))
        self._refresh_import_btn()

    def _refresh_import_btn(self):
        any_checked = any(
            self._tree.item(iid, "values")[0] == self._CHK_ON
            for iid, _, _ in self._rows
        )
        self._import_btn.config(state=tk.NORMAL if any_checked else tk.DISABLED)

    # ── Import ────────────────────────────────────────────────────────────────

    def _import_selected(self):
        if self._session is None:
            messagebox.showerror(
                "Keine Verbindung",
                "Keine aktive Tralgo-Sitzung.\n"
                "Bitte Firefox-Cookie prüfen und App neu starten.",
                parent=self.win,
            )
            return

        to_send = [
            (iid, trade)
            for iid, trade, _ in self._rows
            if self._tree.item(iid, "values")[0] == self._CHK_ON
        ]
        if not to_send:
            messagebox.showinfo("Hinweis", "Keine Trades ausgewählt.", parent=self.win)
            return

        self._import_btn.config(state=tk.DISABLED)
        self._search_btn.config(state=tk.DISABLED)
        self._status_lbl.config(text=f"Importiere {len(to_send)} Trade(s)…")

        def _run():
            ok = err = 0
            for iid, trade in to_send:
                try:
                    send_trade(self._session, trade, self._api_key)
                    self._on_imported(trade["trade_id"])
                    ok += 1
                    self.win.after(0, lambda i=iid, t=trade: self._mark_ok(i, t))
                except Exception as e:
                    err += 1
                    self.win.after(0, lambda i=iid, msg=str(e): self._mark_err(i, msg))
            summary = f"Fertig: {ok} importiert"
            if err:
                summary += f", {err} Fehler"
            self.win.after(0, lambda: self._status_lbl.config(text=summary))
            self.win.after(0, lambda: self._search_btn.config(state=tk.NORMAL))
            # Re-enable import for any rows that still failed
            remaining = any(
                not is_sent and self._tree.item(r_iid, "values")[0] == self._CHK_ON
                for r_iid, _, is_sent in self._rows
            )
            if remaining:
                self.win.after(0, lambda: self._import_btn.config(state=tk.NORMAL))

        threading.Thread(target=_run, daemon=True).start()

    def _mark_ok(self, iid, trade):
        vals = list(self._tree.item(iid, "values"))
        vals[0] = self._CHK_ON
        vals[9] = "✅"
        self._tree.item(iid, values=vals, tags=("ok",))

    def _mark_err(self, iid, msg):
        vals = list(self._tree.item(iid, "values"))
        vals[9] = f"❌ {msg[:30]}"
        self._tree.item(iid, values=vals, tags=("err",))


# ── Main App ──────────────────────────────────────────────────────────────────

class App:
    def __init__(self):
        self.root = tk.Tk()
        self.root.withdraw()
        self.root.title("GizerBridge")
        self.root.tk.call("wm", "iconname", self.root, "gizerbridge")
        if _PIL_AVAILABLE:
            try:
                self._wm_icons = [
                    ImageTk.PhotoImage(_load_icon(_ICON_PATH, sz))
                    for sz in (256, 128, 64, 48, 32)
                ]
                self.root.iconphoto(True, *self._wm_icons)
            except Exception:
                pass
        self.config  = load_config()
        self.sent    = load_sent()
        self.session = None
        self._status_color = "yellow"
        self._status_msg   = "Initialisierung..."
        self._log_entries  = []
        self._log_lock     = threading.Lock()
        self._stop         = threading.Event()
        self._queue        = queue.Queue()
        self._monitor_thread = None

    # ── Public entry point ────────────────────────────────────────────────────

    def run(self):
        self.root.after(200, self._process_queue)

        if not self.config.get("api_key"):
            self.root.after(100, lambda: ConfigWindow(
                self.root, self.config, self._on_config_saved))
        else:
            self._start_monitor()

        self._show_main_window()
        self.root.mainloop()

    def _set_icon_color(self, color):
        self._status_color = color

    # ── Main window ───────────────────────────────────────────────────────────

    def _show_main_window(self):
        if hasattr(self, "_fallback_status"):
            return

        self.root.deiconify()
        self.root.title("GizerBridge")
        self.root.resizable(True, True)
        self.root.minsize(420, 140)

        # ── Header ──
        hdr = tk.Frame(self.root, padx=14, pady=10)
        hdr.pack(fill=tk.X)
        if _PIL_AVAILABLE:
            try:
                self._hdr_photo = ImageTk.PhotoImage(_load_icon(_ICON_FULL_PATH, 128))
                tk.Label(hdr, image=self._hdr_photo).pack(side=tk.LEFT)
            except Exception:
                tk.Label(hdr, text="GizerBridge",
                         font=("", 13, "bold")).pack(side=tk.LEFT)
        else:
            tk.Label(hdr, text="GizerBridge",
                     font=("", 13, "bold")).pack(side=tk.LEFT)

        # ── Status row ──
        sf = tk.Frame(self.root, padx=14)
        sf.pack(fill=tk.X)
        self._fallback_dot = tk.Label(sf, text="●", fg="goldenrod",
                                       font=("", 11))
        self._fallback_dot.pack(side=tk.LEFT)
        self._fallback_status = tk.Label(sf, text=self._status_msg,
                                          anchor="w", wraplength=360)
        self._fallback_status.pack(side=tk.LEFT, padx=(5, 0))

        # ── Buttons ──
        bf = tk.Frame(self.root, padx=14, pady=12)
        bf.pack(fill=tk.X)
        for text, cmd in [
            ("Manueller Import", self._open_manual_import),
            ("Einstellungen",    self._open_config),
            ("Log",              self._open_log),
            ("Beenden",          self._quit),
        ]:
            tk.Button(bf, text=text, width=15,
                      command=cmd).pack(side=tk.LEFT, padx=(0, 6))

        self.root.protocol("WM_DELETE_WINDOW", self._quit)

    # ── Monitor thread ────────────────────────────────────────────────────────

    def _start_monitor(self):
        self._stop.clear()
        self._monitor_thread = threading.Thread(
            target=self._monitor_loop, daemon=True)
        self._monitor_thread.start()

    def _monitor_loop(self):
        self._queue.put(lambda: self._set_icon_color("yellow"))

        cfg = self.config
        if not cfg.get("api_key"):
            self._log("Konfiguration unvollständig — bitte Einstellungen öffnen.")
            self._queue.put(lambda: self._set_icon_color("red"))
            return

        cookies, err = find_cookies()
        if err:
            self._log(f"Cookie-Fehler: {err}")
            self._queue.put(lambda: self._set_icon_color("red"))
            self._queue.put(lambda: messagebox.showwarning(
                "Tralgo – Cookie fehlt", err, parent=self.root))
            return

        self.session = build_session(cookies)

        host        = cfg.get("dtc_host", "localhost")
        port        = cfg.get("dtc_port", 11099)
        accounts    = cfg.get("accounts", [])
        instruments = self._active_instruments(cfg)

        def _on_trade(trade):
            if trade["trade_id"] in self.sent:
                return
            try:
                send_trade(self.session, trade, cfg["api_key"])
                self.sent.add(trade["trade_id"])
                save_sent(self.sent)
                msg = (f"Übertragen: {trade['instrument']} {trade['direction']} "
                       f"Entry {trade['entry_price']} → Exit {trade['exit_price']} "
                       f"| PnL {trade['pnl']} USD ({trade['pnl_ticks']} Ticks)")
                self._log(msg)
                self._update_status_msg(msg)
                self._queue.put(lambda: self._set_icon_color("green"))
            except Exception as e:
                err_msg = f"Übertragungsfehler ({trade['instrument']}): {e}"
                self._log(err_msg)
                self._update_status_msg(err_msg)
                self._queue.put(lambda: self._set_icon_color("red"))

        if not accounts:
            self._log("Keine Konten konfiguriert.")
            return

        self._log(f"Verbinde mit Sierra Chart DTC {host}:{port}…")

        def _on_ready(existing_ids):
            self.sent.update(existing_ids)
            save_sent(self.sent)
            self._log("Bereit. Überwache SC via DTC auf neue Trades…")
            self._queue.put(lambda: self._set_icon_color("green"))
            self._update_status_msg("Läuft – warte auf neue Trades (DTC)")

        result = monitor_realtime(
            host, port, accounts, instruments,
            on_trade=_on_trade,
            stop_event=self._stop,
            log_fn=self._log,
            on_ready=_on_ready,
        )

        if result is None:
            self._queue.put(lambda: self._set_icon_color("red"))
            self._update_status_msg("DTC-Verbindung fehlgeschlagen – Sierra Chart läuft?")
        else:
            self._log("DTC-Monitor beendet.")

    @staticmethod
    def _active_instruments(cfg):
        return {k: v for k, v in cfg.get("instruments", {}).items()
                if v.get("enabled", True)}

    # ── Queue / status helpers ────────────────────────────────────────────────

    def _process_queue(self):
        try:
            while True:
                self._queue.get_nowait()()
        except queue.Empty:
            pass
        if hasattr(self, "_fallback_status"):
            lbl_fg = {"green": "darkgreen", "red": "#cc0000", "yellow": "darkorange"}
            dot_fg = {"green": "#00b300",   "red": "#cc0000", "yellow": "goldenrod"}
            self._fallback_status.config(
                text=self._status_msg,
                fg=lbl_fg.get(self._status_color, "gray"))
            self._fallback_dot.config(
                fg=dot_fg.get(self._status_color, "gray"))
        self.root.after(300, self._process_queue)

    def _log(self, msg):
        entry = f"[{datetime.now().strftime('%H:%M:%S')}] {msg}"
        with self._log_lock:
            self._log_entries.append(entry)
            if len(self._log_entries) > 200:
                self._log_entries.pop(0)

    def _update_status_msg(self, msg):
        self._status_msg = msg

    # ── GUI windows ───────────────────────────────────────────────────────────

    def _open_status(self):
        win = tk.Toplevel(self.root)
        win.title("Status")
        win.resizable(False, False)
        colors = {"green": "darkgreen", "red": "red", "yellow": "darkorange"}
        labels = {"green": "Läuft", "red": "Fehler", "yellow": "Initialisierung"}
        tk.Label(win, text=f"● {labels.get(self._status_color, '')}",
                 fg=colors.get(self._status_color, "gray"),
                 font=("", 12, "bold")).pack(padx=20, pady=(14, 4))
        tk.Label(win, text=self._status_msg, wraplength=380).pack(padx=20, pady=4)
        tk.Label(win, text=f"Übertragene Trades gesamt: {len(self.sent)}",
                 fg="gray").pack(padx=20, pady=4)
        with self._log_lock:
            last = self._log_entries[-1] if self._log_entries else "—"
        tk.Label(win, text=f"Letzter Log-Eintrag:\n{last}",
                 wraplength=380, fg="gray", justify=tk.LEFT).pack(padx=20, pady=(4, 14))
        tk.Button(win, text="Schließen", width=10, command=win.destroy).pack(pady=(0, 12))

    def _open_log(self):
        win = tk.Toplevel(self.root)
        win.title("Log – letzte 50 Einträge")
        win.geometry("620x400")
        frame = tk.Frame(win)
        frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
        sb = tk.Scrollbar(frame)
        sb.pack(side=tk.RIGHT, fill=tk.Y)
        txt = tk.Text(frame, yscrollcommand=sb.set, state=tk.DISABLED,
                      font=("Monospace", 8), wrap=tk.NONE)
        txt.pack(fill=tk.BOTH, expand=True)
        sb.config(command=txt.yview)
        with self._log_lock:
            entries = list(self._log_entries[-50:])
        txt.config(state=tk.NORMAL)
        txt.insert(tk.END, "\n".join(entries))
        txt.config(state=tk.DISABLED)
        txt.see(tk.END)
        tk.Button(win, text="Schließen", command=win.destroy).pack(pady=6)

    def _open_config(self):
        ConfigWindow(self.root, self.config, self._on_config_saved)

    def _open_manual_import(self):
        ManualImportWindow(
            self.root,
            self.config,
            self.sent,
            self.session,
            self.config.get("api_key", ""),
            self._on_trade_manually_imported,
        )

    def _on_trade_manually_imported(self, trade_id):
        self.sent.add(trade_id)
        save_sent(self.sent)

    def _on_config_saved(self, new_cfg):
        self.config = new_cfg
        save_config(new_cfg)
        self._stop.set()
        if self._monitor_thread and self._monitor_thread.is_alive():
            self._monitor_thread.join(timeout=5)
        self._start_monitor()

    def _quit(self):
        self._stop.set()
        save_sent(self.sent)
        try:
            self.root.quit()
            self.root.destroy()
        except tk.TclError:
            pass
