"""
Gold AutoTrade — Windows EXE Launcher
Entry point for the PyInstaller-packaged executable.
Auto-activates trial license, then starts the GUI backend engine.
"""
from __future__ import annotations

import os
import runpy
import sys
import time
import traceback
import json
import urllib.request
from pathlib import Path


def _is_frozen() -> bool:
    return getattr(sys, 'frozen', False)


def _startup_log_path() -> Path:
    if sys.platform == 'win32':
        base = Path(os.environ.get('APPDATA', os.path.expanduser('~'))) / 'GoldAutoTrade'
    else:
        base = Path(os.environ.get('HOME', os.path.expanduser('~'))) / 'GoldAutoTrade'
    base.mkdir(parents=True, exist_ok=True)
    (base / 'logs').mkdir(parents=True, exist_ok=True)
    return base / 'logs' / 'launcher.log'


def _log(msg: str) -> None:
    try:
        p = _startup_log_path()
        ts = time.strftime('%Y-%m-%dT%H:%M:%S')
        with open(p, 'a', encoding='utf-8') as f:
            f.write(f'[{ts}] {msg}\n')
    except Exception:
        pass


def _show_error(title: str, message: str) -> None:
    """Show error to user. In frozen mode uses MessageBox; in dev mode prints."""
    if _is_frozen() and sys.platform == 'win32':
        try:
            import ctypes
            ctypes.windll.user32.MessageBoxW(0, message, title, 0x10)  # MB_ICONERROR
        except Exception:
            pass
    else:
        print(f'\n  [{title}] {message}\n')


def _print_banner() -> None:
    print()
    print("  ╔══════════════════════════════════════════╗")
    print("  ║        Gold AutoTrade                    ║")
    print("  ║        14-Day Free Trial                 ║")
    print("  ╚══════════════════════════════════════════╝")
    print()


def _activate_trial() -> bool:
    """Check license validity. Works with both paid and trial licenses.

    Priority: 1) existing valid license  2) auto-trial claim.
    """
    try:
        from src.license.validator import load_license, get_license_status

        # First: try loading existing license (paid or trial) without auto-trial
        _log("Checking existing license...")
        lic = load_license(auto_trial=False)
        if lic and lic.get("valid"):
            status = get_license_status()
            remaining = status.get("remaining_days", status.get("days_remaining", "?"))
            tier = lic.get("payload", {}).get("tier", "trial")
            _log(f"License valid — tier={tier} {remaining} days remaining")
            print(f"  [1/2] License OK — {tier} ({remaining} days remaining)")
            return True

        # Second: try auto-trial
        _log("No existing license, trying auto-trial...")
        lic = load_license(auto_trial=True)
        if lic and lic.get("valid"):
            status = get_license_status()
            remaining = status.get("remaining_days", status.get("days_remaining", "?"))
            _log(f"Trial activated — {remaining} days remaining")
            print(f"  [1/2] Activating trial... OK — {remaining} days remaining")
            return True

        _log("License check failed — app will run in restricted mode")
        print("  [1/2] License check... SKIPPED (app will run in restricted mode)")
        return False
    except Exception as exc:
        _log(f"License check exception: {exc}")
        print(f"  [1/2] License check... SKIPPED — {exc}")
        return False


def _start_engine() -> None:
    """Import and run the GUI backend main loop."""
    print("  [2/2] Starting engine...", end=" ", flush=True)

    port = 18790
    try:
        import config_manager
        cfg = config_manager.load_app_config()
        port = int(cfg.get("gui_port", 18790))
    except Exception:
        pass

    print(f"Dashboard: http://127.0.0.1:{port}")
    print()

    _log(f"Starting gui_backend.main() on port {port}")
    from gui_backend import main as _gui_main
    _gui_main()


def _is_daemon_subprocess() -> bool:
    """Check if this process was spawned as a daemon/pipeline/macro subprocess."""
    from src.subprocess_util import DAEMON_SUBPROCESS_FLAGS
    return any(flag in sys.argv for flag in DAEMON_SUBPROCESS_FLAGS)


def _bind_runtime_app_dir(app_dir: Path) -> Path:
    resolved = Path(app_dir).resolve()
    os.environ["LOBSTERQUANT_APP_DIR"] = str(resolved)
    return resolved


def _acquire_early_singleton() -> bool:
    """Acquire a kernel mutex at launcher entry to prevent duplicate window races.

    This is a separate mutex from the gui_backend singleton lock. It is checked
    BEFORE trial activation / health checks / heavy imports, shrinking the race
    window from 3-10 seconds to <100ms.

    Returns True if this process is the first/only one.
    """
    if sys.platform != "win32":
        return True
    import ctypes as _ct
    _MUTEX_NAME = "Global\\GoldAutoTrade_Launcher"
    _ERROR_ALREADY_EXISTS = 183
    _k32 = _ct.windll.kernel32
    _k32.CreateMutexW.argtypes = [_ct.c_void_p, _ct.c_bool, _ct.c_wchar_p]
    _k32.CreateMutexW.restype = _ct.c_void_p
    _k32.GetLastError.restype = _ct.c_ulong
    _h = _k32.CreateMutexW(None, False, _MUTEX_NAME)
    if _k32.GetLastError() == _ERROR_ALREADY_EXISTS:
        if _h:
            _k32.CloseHandle(_h)
        return False
    return True


def main() -> None:
    _log(f"Launcher started, frozen={_is_frozen()}, argv={sys.argv[:4]}")

    subprocess_marker = os.environ.get("GAT_SUBPROCESS", "").strip() == "1"

    # Daemon subprocesses skip the launcher UI.
    # --daemon-pipeline runs pipeline.main() directly (same behavior as local dev mode),
    # avoiding the entire gui_backend.main() initialization which can trigger side effects.
    if "--daemon-pipeline" in sys.argv:
        _log("--daemon-pipeline detected, running pipeline.main() directly")
        gate_bypassed = os.environ.get("GAT_RUNTIME_GATE_PASSED", "").strip() == "1"
        if not gate_bypassed:
            try:
                import config_manager as _cm
                from src.startup_gate import run_runtime_startup_gate

                _app_dir = _bind_runtime_app_dir(
                    _cm.get_app_dir() if _is_frozen() else Path(__file__).resolve().parent
                )
                gate = run_runtime_startup_gate(project_root=_app_dir, app_dir=_app_dir, current_pid=os.getpid())
                if not gate.get("ok", False):
                    runtime_mode = str(gate.get("runtime_mode", "BOOTSTRAP_REPAIR") or "BOOTSTRAP_REPAIR")
                    failures = "; ".join(gate.get("failures", [])[:3]) or "runtime startup gate blocked daemon launch"
                    _log(f"--daemon-pipeline blocked by runtime gate mode={runtime_mode}: {failures}")
                    _show_error(
                        "Trading Runtime Blocked",
                        f"交易运行时未通过启动保护。\n\n"
                        f"当前状态: {runtime_mode}\n"
                        f"原因: {failures}\n\n"
                        f"请先从主界面完成修复后再启动交易运行。"
                    )
                    sys.exit(2)
            except Exception as _exc:
                _log(f"--daemon-pipeline gate failed before launch: {_exc}\n{traceback.format_exc()}")
                _show_error(
                    "Trading Runtime Guard Failed",
                    f"交易运行时启动保护检查失败。\n\n"
                    f"错误: {_exc}\n\n"
                    f"请从主界面重新启动。"
                )
                sys.exit(2)
        from pathlib import Path as _Path
        import config_manager as _cm
        _app_dir = _bind_runtime_app_dir(
            _cm.get_app_dir() if _is_frozen() else _Path(__file__).resolve().parent
        )
        (_app_dir / "data" / "inbox").mkdir(parents=True, exist_ok=True)
        (_app_dir / "data" / "outbox").mkdir(parents=True, exist_ok=True)
        # Build argv for argparse compatibility (same as gui_backend handler)
        import os as _os
        _src = _app_dir / "src" if _is_frozen() else _Path(__file__).resolve().parent
        _save = list(sys.argv)
        sys.argv = [str(_src / "pipeline.py"), "--source", "ea_feed", "--runtime-intent", "live_trade",
                     "--tenant-id", "default", "--execution-mode", "ea_bridge",
                     "--symbol", "XAUUSD", "--timeframe", "M5",
                     "--input-path", str(_app_dir / "data" / "inbox" / "request.json"),
                     "--output-path", str(_app_dir / "data" / "outbox" / "signal.json"),
                     "--execution-report-path", str(_app_dir / "data" / "inbox" / "execution_report.json"),
                     "--poll-seconds", "2", "--enable-incident-autofix", "true"]
        try:
            from src import pipeline as _pipeline
            _pipeline.main()
        except Exception as _exc:
            _log(f"pipeline.main() crashed: {_exc}\n{traceback.format_exc()}")
            sys.exit(1)
        finally:
            sys.argv = _save
        sys.exit(0)

    if subprocess_marker and len(sys.argv) > 1 and sys.argv[1].lower().endswith(".py"):
        script_path = Path(sys.argv[1]).resolve()
        _log(f"Subprocess script detected, running {script_path.name} directly")
        _save = list(sys.argv)
        sys.argv = sys.argv[1:]
        try:
            runpy.run_path(str(script_path), run_name="__main__")
        except ImportError:
            _log(f"Script {script_path.name} has no __main__, falling through to daemon check")
        finally:
            sys.argv = _save
        return

    if _is_daemon_subprocess():
        _log("Daemon subprocess detected, skipping early singleton and launcher UI")
        from gui_backend import main as _gui_main
        _gui_main()
        return

    if subprocess_marker:
        _log("Subprocess marker detected without script/daemon flags — clearing stale marker and continuing")
        os.environ.pop("GAT_SUBPROCESS", None)

    if not _acquire_early_singleton():
        _log("Early singleton check failed — another GUI instance is already launching")
        sys.exit(0)

    # ── Handle --activate KEY command ──
    _activate_idx = None
    for i, arg in enumerate(sys.argv):
        if arg == "--activate" and i + 1 < len(sys.argv):
            _activate_idx = i
            break
    if _activate_idx is not None:
        key = sys.argv[_activate_idx + 1]
        print(f"  Activating license key: {key}")
        try:
            from src.license.activate import activate as _lic_activate
            _lic_activate(key)
        except Exception as exc:
            print(f"  Activation failed: {exc}")
        sys.exit(0)

    _print_banner()

    _activate_trial()
    # Trial failure no longer blocks startup — user can still access dashboard
    # and configure license/API keys. Trading will be restricted until activated.

    try:
        _start_engine()
    except KeyboardInterrupt:
        _log("KeyboardInterrupt — shutting down")
        print("\n  Shutting down...")
    except Exception as exc:
        _log(f"Engine start failed: {exc}\n{traceback.format_exc()}")
        _show_error(
            "Engine Start Failed",
            f"The trading engine could not start.\n\n"
            f"Error: {exc}\n\n"
            f"Startup log: {_startup_log_path()}\n\n"
            f"Please try reinstalling or contact support."
        )
        sys.exit(1)


if __name__ == "__main__":
    main()
