"""
BizNode Dashboard Launcher
==========================
Run this from the project root to start the owner dashboard.

    python run_dashboard.py

Then open: http://localhost:7777
"""

import sys
import os
import subprocess

# Ensure the project root is on sys.path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

_ROOT = os.path.dirname(os.path.abspath(__file__))

# Load .env so all services (SMTP, IMAP, etc.) pick up correct values
try:
    from dotenv import load_dotenv
    load_dotenv(os.path.join(_ROOT, ".env"), override=False)
except ImportError:
    pass


def _ensure_deps():
    """Auto-install missing Python dependencies from requirements.txt."""
    req_file = os.path.join(_ROOT, "requirements.txt")
    if not os.path.exists(req_file):
        return
    probes = ["langgraph", "langchain_core", "qdrant_client", "apscheduler", "fastapi"]
    missing = [pkg for pkg in probes if not _importable(pkg)]
    if missing:
        print(f"[!] Missing packages: {', '.join(missing)}")
        print("[*] Installing from requirements.txt — please wait...")
        result = subprocess.run(
            [sys.executable, "-m", "pip", "install", "-r", req_file, "--user", "-q"],
            capture_output=False,
        )
        if result.returncode == 0:
            print("[OK] Dependencies installed. Starting dashboard...\n")
        else:
            print("[!] Some packages may have failed to install. Continuing anyway...\n")


def _importable(pkg: str) -> bool:
    try:
        __import__(pkg)
        return True
    except ImportError:
        return False


# ── Machine fingerprint ───────────────────────────────────────────────────────

def _get_machine_fingerprint() -> str:
    """
    Stable hardware fingerprint: SHA-256 of volume serial + MAC + hostname.
    Matches the algorithm used by the PHP server's machine_fingerprint field.
    """
    import hashlib
    import socket
    import uuid

    parts = [socket.gethostname(), str(uuid.getnode())]

    if sys.platform == "win32":
        try:
            r = subprocess.run(
                ["wmic", "os", "get", "SerialNumber", "/value"],
                capture_output=True, text=True, timeout=5,
            )
            for line in r.stdout.splitlines():
                if "SerialNumber=" in line:
                    parts.append(line.split("=", 1)[1].strip())
                    break
        except Exception:
            pass

    return hashlib.sha256("|".join(parts).encode()).hexdigest()


# ── Trial period ──────────────────────────────────────────────────────────────

_TRIAL_DAYS = 10
_TRIAL_FILE = os.path.join(_ROOT, ".trial")


def _check_trial_period() -> tuple[bool, str, int]:
    """
    Check whether the 10-day trial is still active.
    The .trial file stores start date + machine fingerprint so moving the file
    to a different machine renders it invalid.

    Returns (allowed, message, days_remaining).

    Limitation: deleting .trial resets the trial on this machine.
    This is mitigated in a later step by server-side registration + fingerprint
    check at https://biznode.1bz.biz/api/trial/status.
    """
    import json
    from datetime import datetime, timedelta

    fingerprint = _get_machine_fingerprint()

    if not os.path.exists(_TRIAL_FILE):
        # First run — write trial marker
        started    = datetime.utcnow()
        expires_at = started + timedelta(days=_TRIAL_DAYS)
        try:
            with open(_TRIAL_FILE, "w", encoding="utf-8") as f:
                json.dump({
                    "started":     started.isoformat(),
                    "expires":     expires_at.isoformat(),
                    "fingerprint": fingerprint,
                }, f)
        except OSError:
            pass
        return True, f"Trial started — {_TRIAL_DAYS} days remaining.", _TRIAL_DAYS

    try:
        with open(_TRIAL_FILE, encoding="utf-8") as f:
            data = json.load(f)

        # Fingerprint must match this machine (skip check inside Docker)
        in_docker = os.path.exists("/.dockerenv") or os.environ.get("DOCKER_MODE") == "1"
        if not in_docker and data.get("fingerprint") != fingerprint:
            return False, "Trial file was created on a different machine.", 0

        expires = datetime.fromisoformat(data["expires"])
        now     = datetime.utcnow()
        if now < expires:
            days_left = max(0, (expires - now).days)
            return True, f"Trial active — {days_left} day(s) remaining.", days_left
        else:
            return False, "Trial period has expired.", 0

    except Exception as e:
        return False, f"Trial data unreadable: {e}", 0


# ── License gate ──────────────────────────────────────────────────────────────

def _check_license():
    """
    Enforcement gate — must pass before any heavy imports load.

    Priority:
      1. Valid USB license  → allow (Pro mode)
      2. Trial still active → allow (Trial mode, with days warning)
      3. Neither            → HARD EXIT
    """
    from services.license_validator import validate_license

    # ── 1. USB license ────────────────────────────────────────────────────────
    result = validate_license()
    if result.valid:
        tag = " [DEV MODE]" if result.dev_mode else ""
        print(f"[LICENSE] Valid{tag} — Order: {result.order_id} | "
              f"Expires: {result.expiry} ({result.days_remaining} days left)")
        return result

    # ── 2. Trial period ───────────────────────────────────────────────────────
    trial_ok, trial_msg, trial_days = _check_trial_period()
    if trial_ok:
        print(f"[TRIAL]   {trial_msg}")
        if trial_days <= 3:
            print(f"[TRIAL]   WARNING: {trial_days} day(s) left — purchase at https://biznode.1bz.biz")
        return result   # result.valid=False but we allow; caller only checks blocking

    # ── 3. BLOCKED ────────────────────────────────────────────────────────────
    print()
    print("=" * 60)
    print("  BIZNODE — Trial Expired")
    print("=" * 60)
    print()
    print("  Insert your BizNode Pro USB to continue.")
    print()
    print("  Purchase : https://biznode.1bz.biz")
    print("  Support  : biznode@1bz.biz")
    print()
    print("=" * 60)
    print()
    sys.exit(1)


# ── Port helper ───────────────────────────────────────────────────────────────

def _get_port() -> int:
    """Read DASHBOARD_PORT from .env (fallback to 7777)."""
    env_file = os.path.join(_ROOT, ".env")
    if os.path.exists(env_file):
        with open(env_file, encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if line.startswith("DASHBOARD_PORT="):
                    try:
                        return int(line.split("=", 1)[1].strip())
                    except ValueError:
                        pass
    return int(os.environ.get("DASHBOARD_PORT", 7777))


# ── Entry point ───────────────────────────────────────────────────────────────

if __name__ == "__main__":
    _ensure_deps()

    # Validate BEFORE importing uvicorn / FastAPI / LangGraph / everything else
    _check_license()

    import uvicorn

    port = _get_port()
    print("=" * 50)
    print("  BizNode Dashboard")
    print(f"  http://localhost:{port}")
    print("=" * 50)
    in_docker = os.path.exists("/.dockerenv") or os.environ.get("DOCKER_MODE") == "1"
    host = "0.0.0.0" if in_docker else "127.0.0.1"
    uvicorn.run(
        "ui.server:app",
        host=host,
        port=port,
        reload=False,
        log_level="info",
    )
