#!/usr/bin/env python3
"""
Gold AutoTrade — One-Click Trial Launcher

This is what trial users run. It:
  1. Auto-detects MT5 installation
  1. Auto-detects MT5 installation
  2. Configures AI provider API key (supports 10+ international providers)
  3. Auto-claims a 14-day trial license (no server, no signup)
  4. Risk disclosure compliance
  5. Starts paper trading engine + AI macro direction daemon
  6. Launches the GUI dashboard
  7. Opens the browser to the dashboard

Usage:
  python3 start_trial.py
  python3 start_trial.py --live    (only after onboarding complete)
"""
from __future__ import annotations

import json
import os
import subprocess
import sys
import time
from pathlib import Path

ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT))

from src.subprocess_util import popen as _safe_popen

# frozen: PyInstaller onefile — sys._MEIPASS contains the bundled _internal tree
_INTERNAL_ROOT: Path | None = None
if getattr(sys, 'frozen', False):
    _INTERNAL_ROOT = Path(sys._MEIPASS)
    sys.path.insert(0, str(_INTERNAL_ROOT))

# ─── ASCII Banner ──────────────────────────────────────────────────

BANNER = r"""
   ____       _     _         _                  _
  / ___| ___ | | __| |       / \   _ __ ___     / \   _ __  _ __
 | |  _ / _ \| |/ _| |_____ / _ \ | '_ ` _ \   / _ \ | '_ \| '_ \
 | |_| | (_) | | |_| |_____/ ___ \| | | | | | / ___ \| |_) | |_) |
  \____|\___/|_|\__|     /_/   \_\_| |_| |_|/_/   \_\ .__/| .__/
                                                     |_|   |_|
"""


def main():
    print(BANNER)
    print("Gold AutoTrade — 14-Day Free Trial")
    print("=" * 55)
    print()

    # ── Step 1: MT5 Detection ──────────────────────────────────────
    print("[1/7] Detecting MT5 installation...")
    mt5_path, mt5_found = _detect_mt5()
    if mt5_found:
        print(f"  OK MT5 found: {mt5_path}")
    else:
        print(f"  ! MT5 not auto-detected.")
        print(f"  Expected at: {mt5_path}")
        print(f"  Make sure MT5 is running with the Bridge EA installed.")
    print()

    # ── Step 2: AI Provider API Key ─────────────────────────────────
    print("[2/7] Configuring AI provider API key...")
    _ensure_api_key()
    print()

    # ── Step 3: Trial License ──────────────────────────────────────
    print("[3/7] Activating 14-day trial license...")
    try:
        from src.license.trial import claim_trial, get_trial_status
        trial_status = get_trial_status()

        if trial_status["status"] == "TRIAL_ACTIVE":
            print(f"  OK Trial active — {trial_status['days_left']} days remaining")
        elif trial_status["status"] == "PAID_LICENSE":
            print(f"  OK Paid license active — tier: {trial_status.get('tier', 'pro')}")
        elif trial_status["status"] == "TRIAL_EXPIRED":
            print(f"  X Trial expired.")
            _print_purchase_info()
            _wait_and_exit(1)
        else:
            result = claim_trial()
            if result["ok"]:
                print(f"  OK Trial activated — {result['days_left']} days remaining")
                print(f"    Expires: {result['expires']}")
            else:
                print(f"  X Cannot activate trial: {result.get('message', 'Unknown error')}")
                _wait_and_exit(1)
    except ImportError as e:
        print(f"  X License module error: {e}")
        _wait_and_exit(1)
    print()

    # ── Step 4: Check for updates + sync configs ─────────────────────
    print("[4/7] Checking for updates...")
    _check_updates_and_sync_configs()
    print()

    # ── Telemetry startup ping (silent) ───────────────────────────────
    try:
        from src.telemetry.tracker import report_startup
        trial_status = get_trial_status()
        report_startup(
            version=_read_version(),
            license_status=trial_status.get("status", "unknown"),
            trial_days_left=trial_status.get("days_left", -1),
        )
    except Exception:
        pass  # never block startup on telemetry

    # ── Step 5: Compliance ─────────────────────────────────────────
    print("[5/7] Risk disclosure...")
    try:
        from src.compliance.disclaimers import has_accepted_risk_disclosure, RISK_DISCLOSURE_V1
        if not has_accepted_risk_disclosure():
            print()
            print(RISK_DISCLOSURE_V1)
            print()
            response = input("I have read and accept the risk disclosure (yes/no): ").strip().lower()
            if response in ("yes", "y"):
                from src.compliance.disclaimers import accept_risk_disclosure
                accept_risk_disclosure()
                print("  OK Risk disclosure accepted")
            else:
                print("  X You must accept the risk disclosure to use this software.")
                _wait_and_exit(1)
        else:
            print("  OK Risk disclosure previously accepted")
    except ImportError:
        print("  ! Compliance module not available — skipping")
    print()

    # ── Step 6: Start Engines ──────────────────────────────────────
    print("[6/7] Starting trading engines...")
    # === System Health Check (Codex) ===
    try:
        from src.health_check_startup import check_func, FIX_GUIDE
        failed_checks = []
        for name, fn in check_func.items():
            try:
                if not fn():
                    failed_checks.append((name, FIX_GUIDE.get(name, "")))
            except Exception:
                failed_checks.append((name, FIX_GUIDE.get(name, "")))
        if failed_checks:
            print("\n  ========================================")
            print("  Deployment Health Check: ISSUES FOUND")
            print("  ========================================")
            for name, fix in failed_checks:
                print(f"  [{name}] FAIL")
                print(f"    -> {fix}")
            print("  The system will continue, but some features may be limited.")
            print("  ========================================\n")
    except Exception as e:
        print(f"  Health check unavailable: {e}")
    # === End Health Check ===
    _start_engines()
    print()

    # ── Step 6: Launch GUI Dashboard ─────────────────────────────
    print("[7/7] Launching dashboard...")
    gui_url = "http://127.0.0.1:18790"
    try:
        gui_entry = str(_resolve_script("", "gui_backend.py"))
        reused_existing_gui = _wait_for_http_ready(f"{gui_url}/api/ping", timeout_seconds=2)
        ready = reused_existing_gui
        if reused_existing_gui:
            print(f"  OK Dashboard already running: {gui_url}")
        else:
            env = os.environ.copy()
            env.setdefault("PYTHONPATH", str(ROOT))
            env["GUI_PORT"] = "18790"
            _launch_background(
                [sys.executable, gui_entry],
                ROOT / "logs" / "gui_backend.log",
                env=env,
            )
            ready = _wait_for_http_ready(f"{gui_url}/api/ping", timeout_seconds=25)
        if ready:
            print(f"  OK Dashboard: {gui_url}")
        else:
            print(f"  ! Dashboard is still starting: {gui_url}")
        _open_browser(gui_url)
    except Exception as e:
        print(f"  ! GUI launch issue: {e}")

    # ── Done ───────────────────────────────────────────────────────
    print()
    print("=" * 55)
    print("Trial Ready!")
    print()
    print(f"  Dashboard:     {gui_url}")
    print(f"  Engine log:    tail -f logs/engine.log")
    print(f"  Daemon log:    tail -f logs/daemon.log")
    print(f"  GUI log:       tail -f logs/gui_backend.log")
    print(f"  Add license:   python3 -m src.license.activate --key <key>")
    print()
    print(f"  Trial expires in 14 days. No credit card required.")
    print(f"  WeChat/QQ: goldautotrade | Email: support@goldauto.top")
    print("=" * 55)

    # Keep running — periodically check license validity online
    _last_online_check = 0
    ONLINE_CHECK_SECONDS = 3600  # check revocation every hour
    try:
        while True:
            time.sleep(60)
            now = time.time()
            if now - _last_online_check >= ONLINE_CHECK_SECONDS:
                _last_online_check = now
                try:
                    from src.license.validator import check_online
                    from src.license.activate import DEFAULT_SERVER
                    result = check_online(DEFAULT_SERVER)
                    if result.get("error"):
                        print(f"\n  ! License issue: {result['error']}")
                        print(f"  WeChat/QQ: goldautotrade | Email: support@goldauto.top\n")
                except Exception:
                    pass  # offline — silently allow
    except KeyboardInterrupt:
        print("\nShutting down. Thanks for trying Gold AutoTrade!")
        sys.exit(0)


# ─── Helpers ────────────────────────────────────────────────────────

def _resolve_script(*segments: str) -> Path:
    """Resolve a script path, preferring _internal/ root when frozen."""
    if _INTERNAL_ROOT is not None:
        candidate = _INTERNAL_ROOT.joinpath(*segments)
        if candidate.exists():
            return candidate
    return ROOT.joinpath(*segments)

def _resolve_data(*segments: str) -> Path:
    """Resolve a data file path, preferring config_manager data dir when frozen."""
    if _INTERNAL_ROOT is not None:
        try:
            import config_manager
            data_dir = config_manager.get_data_dir()
            candidate = data_dir.joinpath(*segments)
            candidate.parent.mkdir(parents=True, exist_ok=True)
            return candidate
        except Exception:
            pass
    return ROOT.joinpath(*segments)


def _detect_mt5() -> tuple[str, bool]:
    """Auto-detect MT5 terminal path."""
    home = Path.home()
    candidates = [
        # macOS (Wine/CrossOver)
        home / "Library" / "Application Support" / "net.metaquotes.wine.metatrader5",
        # Windows
        Path("C:/Program Files/MetaTrader 5"),
        Path("C:/Program Files (x86)/MetaTrader 5"),
        # Custom
        Path(os.environ.get("MT5_PATH", "")),
    ]
    for c in candidates:
        if c.exists():
            return str(c), True
    return str(candidates[0]), False


def _ensure_api_key() -> None:
    """Ensure at least one AI provider has an API key configured.

    Priority: check openclaw.json providers → check app_config.json (legacy) →
    prompt user to pick a provider and enter their key.
    """
    # Check if any provider already has a key configured
    provider_status = _check_all_provider_keys()
    configured = {k: v for k, v in provider_status.items() if v["has_key"]}
    if configured:
        names = ", ".join(configured.keys())
        print(f"  OK API key(s) configured: {names}")
        return

    # No key found — guide user through setup
    print("  An AI provider API key is required for AI-driven trading decisions.")
    print()
    print("  Supported providers:")
    providers_list = list(provider_status.keys())
    for i, name in enumerate(providers_list[:8], 1):
        rec = " ← 推荐 (国内访问快)" if name == "deepseek" else ""
        print(f"    {i}. {name}{rec}")
    print()
    print("  If your preferred provider is not listed, you can add it later in the GUI.")
    print()

    choice = input(f"  Choose provider [1-{min(len(providers_list), 8)}, default=1]: ").strip()
    try:
        idx = int(choice) - 1 if choice else 0
        if idx < 0 or idx >= len(providers_list):
            idx = 0
    except ValueError:
        idx = 0
    provider = providers_list[idx]

    keys_url_map = {
        "deepseek": "https://platform.deepseek.com/api_keys",
        "openai": "https://platform.openai.com/api-keys",
        "anthropic": "https://console.anthropic.com/settings/keys",
        "google": "https://aistudio.google.com/apikey",
        "groq": "https://console.groq.com/keys",
        "together": "https://api.together.ai/settings/api-keys",
        "mistral": "https://console.mistral.ai/api-keys",
        "xai": "https://console.x.ai",
    }
    key_url = keys_url_map.get(provider, f"https://platform.{provider}.com")
    print(f"  Get your {provider} API key at: {key_url}")
    print()
    api_key = input(f"  Paste your {provider} API key: ").strip()
    if not api_key:
        print("  ! No API key provided. AI trading will fall back to technical analysis only.")
        print("    You can configure it later in the Dashboard → 大模型配置.")
        return

    # Save to openclaw.json for the provider
    try:
        _save_provider_api_key(provider, api_key)
        print(f"  OK API key saved for {provider}")

        # Mark onboarding step
        try:
            from src.onboarding.wizard import save_api_key_config
            save_api_key_config(provider)
        except Exception:
            pass
    except Exception as e:
        print(f"  ! Could not save to openclaw.json: {e}")
        # Fallback: save via unified function (handles app_config fallback internally)
        try:
            import config_manager
            config_manager.save_provider_api_key(provider, api_key)
            print(f"  OK Key saved via fallback")
        except Exception as e2:
            print(f"  ! Could not save API key: {e2}")


def _check_all_provider_keys() -> dict[str, dict]:
    """Return dict of {provider_name: {has_key: bool, models: [...]}} from openclaw.json."""
    result = {}
    try:
        openclaw_config = Path.home() / ".openclaw" / "openclaw.json"
        if openclaw_config.exists():
            cfg = json.loads(openclaw_config.read_text(encoding="utf-8"))
            providers = cfg.get("models", {}).get("providers", {})
            for name, info in providers.items():
                # Skip local-only providers (omlx has no apiKey concept)
                if name in ("omlx",):
                    continue
                has_key = bool(info.get("apiKey", ""))
                models = info.get("models", [])
                result[name] = {"has_key": has_key, "models": models[:3]}
    except Exception:
        pass

    return result


def _save_provider_api_key(provider: str, api_key: str) -> None:
    """Save API key to openclaw.json for the given provider."""
    import config_manager
    config_manager.save_provider_api_key(provider, api_key)


def _start_engines() -> None:
    """Start pipeline.py (signal engine) and gold_trader.py (AI macro daemon)."""
    # ── Pipeline (signal generation + dispatch) ──
    pipeline_started = False
    pipeline_pid_file = ROOT / "logs" / "pipeline.pid"
    try:
        if pipeline_pid_file.exists():
            try:
                pid = int(pipeline_pid_file.read_text(encoding="utf-8").strip())
                os.kill(pid, 0)
                print(f"  OK Pipeline already running (PID: {pid})")
                pipeline_started = True
            except (ValueError, OSError):
                pass

        if not pipeline_started:
            _pipeline = _resolve_script("src", "pipeline.py")
            _inbox = _resolve_data("data", "inbox", "request.json")
            _outbox = _resolve_data("data", "outbox", "signal.json")
            cmd = [
                sys.executable, str(_pipeline),
                "--source", "mt5",
                "--runtime-intent", "paper_trade",
                
                "--timeframe", "M5",
                "--execution-mode", "mock",
                "--poll-seconds", "5",
                "--enable-incident-autofix", "true",
                "--input-path", str(_inbox),
                "--output-path", str(_outbox),
            ]
            _launch_background(cmd, ROOT / "logs" / "engine.log")
            print(f"  OK Pipeline started (signal engine)")
    except Exception as e:
        print(f"  ! Pipeline start failed: {e}")

    # ── Gold Trader Daemon (AI macro direction + watchdog + bridge) ──
    daemon_started = False
    daemon_pid_file = ROOT / "logs" / "gold_trader.pid"
    try:
        if daemon_pid_file.exists():
            try:
                pid = int(daemon_pid_file.read_text(encoding="utf-8").strip())
                os.kill(pid, 0)
                print(f"  OK Gold Trader daemon already running (PID: {pid})")
                daemon_started = True
            except (ValueError, OSError):
                pass

        if not daemon_started:
            _gold_trader = _resolve_script("bridge", "gold_trader.py")
            cmd = [
                sys.executable, str(_gold_trader),
                "--fg",
                "--monitor-only",
            ]
            _launch_background(cmd, ROOT / "logs" / "daemon.log")
            print(f"  OK Gold Trader daemon started (AI macro direction + watchdog)")
    except Exception as e:
        print(f"  ! Gold Trader daemon start failed: {e}")

    print(f"    Logs: logs/engine.log  logs/daemon.log")


def _print_purchase_info() -> None:
    """Print license purchase contact info."""
    print()
    print("  To purchase a formal license:")
    print("    WeChat/QQ: goldautotrade")
    print("    Email:     support@goldauto.top")
    print("    Website:   https://license.goldauto.top/license")
    print()


def _launch_background(cmd: list[str], log_path: Path, env: dict = None) -> None:
    """Launch a subprocess in background, logging to file."""
    log_path.parent.mkdir(parents=True, exist_ok=True)
    if env is None:
        env = os.environ.copy()
    env.setdefault("PYTHONPATH", str(ROOT))
    env["PYTHONPATH"] = str(ROOT) + os.pathsep + env.get("PYTHONPATH", "")
    with open(log_path, "a") as log:
        proc = _safe_popen(
            cmd,
            stdout=log,
            stderr=subprocess.STDOUT,
            start_new_session=True,
            env=env,
            cwd=str(ROOT),
        )


def _wait_for_http_ready(url: str, timeout_seconds: int = 25) -> bool:
    """Wait until a local HTTP endpoint responds."""
    import urllib.request

    deadline = time.time() + max(timeout_seconds, 1)
    while time.time() < deadline:
        try:
            with urllib.request.urlopen(url, timeout=2) as response:
                status = int(getattr(response, "status", 200) or 200)
                if 200 <= status < 500:
                    return True
        except Exception:
            pass
        time.sleep(1)
    return False


def _open_browser(url: str) -> None:
    """Open browser to the given URL."""
    import webbrowser
    import time
    time.sleep(2)
    try:
        webbrowser.open(url)
    except Exception:
        pass


def _read_version() -> str:
    vf = ROOT / "VERSION"
    return vf.read_text(encoding="utf-8").strip() if vf.exists() else "0.0.0"


def _check_updates_and_sync_configs() -> None:
    """
    Check for app updates and sync configs from server.
    Non-blocking — failures are silent.
    """
    UPDATE_SERVER = "https://license.goldauto.top"

    # ── Check for app update ──
    try:
        import urllib.request
        req = urllib.request.Request(
            f"{UPDATE_SERVER}/api/update/manifest",
            headers={"User-Agent": "GoldAutoTrade-Trial"},
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            manifest = json.loads(resp.read().decode())
        latest = manifest.get("version", "")
        current = _read_version()
        if latest and _version_newer(latest, current):
            print(f"  Update available! v{latest} (current: v{current})")
            print(f"  Download: https://license.goldauto.top/download/")
        else:
            print(f"  You're up to date (v{current})")
    except Exception:
        print("  (unable to check for updates)")

    # ── Sync config files ──
    config_files = ["strategy_rules_v3.json", "risk_governor_rules.json",
                    "agent_orchestration_rules.json"]
    try:
        # Get server config version
        req = urllib.request.Request(
            f"{UPDATE_SERVER}/api/update/config_version",
            headers={"User-Agent": "GoldAutoTrade-Trial"},
        )
        with urllib.request.urlopen(req, timeout=5) as resp:
            server_cv = json.loads(resp.read().decode()).get("config_version", "")

        if server_cv:
            import hashlib
            for cf in config_files:
                local = ROOT / "config" / cf
                if local.exists():
                    local_hash = hashlib.sha256(local.read_bytes()).hexdigest()[:16]
                    if local_hash != server_cv:
                        # Download updated config
                        req2 = urllib.request.Request(
                            f"{UPDATE_SERVER}/api/update/config/{cf}",
                            headers={"User-Agent": "GoldAutoTrade-Trial"},
                        )
                        with urllib.request.urlopen(req2, timeout=5) as resp2:
                            data = resp2.read().decode()
                        local.write_text(data)
                        print(f"  Config updated: {cf}")
    except Exception:
        pass  # offline — use cached


def _version_newer(a: str, b: str) -> bool:
    def parse(v: str) -> tuple:
        try:
            return tuple(int(x) for x in v.split("-")[0].split(".") if x.isdigit())
        except Exception:
            return (0,)
    return parse(a) > parse(b)


def _wait_and_exit(code: int) -> None:
    print()
    input("Press Enter to exit...")
    sys.exit(code)


if __name__ == "__main__":
    main()
