"""
EEA Agent Facilitator v2.0
Connects any existing agent to the EEA ecosystem at https://api.agenesis.io
"""

import sys
import os
import ast
import json
import re
import subprocess
import importlib
import time
import webbrowser
from pathlib import Path


# ─── Dependency bootstrap ────────────────────────────────────────────────────

def _pip_install(packages: list[str]) -> bool:
    try:
        subprocess.run(
            [sys.executable, "-m", "pip", "install", "--quiet", *packages],
            check=True,
        )
        return True
    except subprocess.CalledProcessError:
        return False


def _ensure_deps(required: list[str]) -> None:
    missing = []
    for pkg in required:
        import_name = pkg.split("[")[0].replace("-", "_")
        try:
            importlib.import_module(import_name)
        except ImportError:
            missing.append(pkg)

    if not missing:
        return

    print(f"  Installing: {', '.join(missing)} ...")
    ok = _pip_install(missing)
    if not ok:
        print("  FAILED — please install manually and re-run.")
        sys.exit(1)


_ensure_deps(["httpx", "fastapi", "uvicorn"])

import httpx  # noqa: E402


# ─── Constants ───────────────────────────────────────────────────────────────

ANON_KEY = (
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    ".eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh5cXdqeGdra3NlZG10ZmdncWx6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODExMjQyNTQsImV4cCI6MjA5NjcwMDI1NH0"
    ".Rzu_lfkHFWIlSOwM7PhtXEYbs31kevPE7aVfPSyPVPk"
)
SERVICE_KEY = (
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    ".eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh5cXdqeGdra3NlZG10ZmdncWx6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc4MTEyNDI1NCwiZXhwIjoyMDk2NzAwMjU0fQ"
    ".alodg3R2sP2pVSz33g5f9Iy52v5SrfqbiCMXPDInc0o"
)
SUPABASE_URL = "https://hyqwjxgkksedmtfggqlz.supabase.co"
EEA_URL = "https://api.agenesis.io"
ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages"

CAPABILITIES_LIST = [
    # Core (v1.0)
    "web_search", "information_retrieval", "summarize", "fact_check",
    "data_analysis", "pattern_recognition", "insights", "financial_analysis",
    "content_writing", "copywriting", "summarization", "translation",
    "classification", "tagging", "categorization", "entity_extraction",
    # Media processing (v2.0)
    "image_to_video", "video_to_video", "image_to_image",
    "voice_cloning", "speech_to_text", "text_to_speech",
    "lip_sync", "video_editing", "audio_processing",
    # Document processing (v2.0)
    "pdf_extraction", "ocr", "document_translation",
    # Code & automation (v2.0)
    "code_runner", "data_pipeline", "web_scraping",
    # Real-world actions (v2.0)
    "trading_execution", "api_caller", "form_filler",
    "browser_automation", "email_sender",
]


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

def _phase(n: int, label: str) -> None:
    print(f"\n[Phase {n}/11] {label}")
    print("-" * 50)


def _ok(msg: str) -> None:
    print(f"  ✓ {msg}")


def _err(msg: str) -> None:
    print(f"  ! {msg}")


def _info(msg: str) -> None:
    print(f"  . {msg}")


def _ask_retry(action: str) -> bool:
    ans = input(f"  Retry {action}? [Y/n]: ").strip().lower()
    return ans in ("", "y", "yes")


# ─── Phase 1 — Banner ────────────────────────────────────────────────────────

def phase1_banner() -> None:
    print("""
+----------------------------------------------+
|   EEA Agent Facilitator v2.0                |
|   Connect your agent to agenesis.io         |
+----------------------------------------------+
""")
    print("This script will:")
    print("  1. Check your environment and install any missing packages.")
    print("  2. Analyze your agent code (read-only, no imports).")
    print("  3. Auto-configure capabilities, pricing, and webhook.")
    print("  4. Create your EEA account and register your agent.")
    print("  5. Generate run_eea_agent.py and validate it starts correctly.")
    print()


# ─── Phase 2 — Environment check ─────────────────────────────────────────────

def phase2_environment() -> None:
    _phase(2, "Environment check")

    major, minor = sys.version_info[:2]
    if (major, minor) < (3, 11):
        _err(f"Python {major}.{minor} detected — Python 3.11+ required.")
        sys.exit(1)
    _ok(f"Python {major}.{minor}")

    try:
        r = httpx.get(f"{EEA_URL}/health", timeout=8)
        if r.status_code < 500:
            _ok(f"Reached {EEA_URL} (HTTP {r.status_code})")
        else:
            _info(f"Server returned HTTP {r.status_code} — continuing anyway")
    except Exception as exc:
        _info(f"Cannot reach {EEA_URL}: {exc} — continuing anyway")

    for pkg in ["httpx", "fastapi", "uvicorn"]:
        try:
            importlib.import_module(pkg.replace("-", "_"))
            _ok(f"{pkg} available")
        except ImportError:
            _err(f"{pkg} still missing after install attempt")


# ─── Phase 3 — Code analysis (NO IMPORT) ─────────────────────────────────────

def _build_file_summary(path: Path) -> str:
    try:
        source = path.read_text(encoding="utf-8", errors="ignore")
    except OSError:
        return ""

    lines = source.splitlines()
    first_30 = "\n".join(lines[:30])
    imports = [ln for ln in lines if ln.startswith("import ") or ln.startswith("from ")]

    func_names: list[str] = []
    class_names: list[str] = []
    try:
        tree = ast.parse(source)
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                func_names.append(node.name)
            elif isinstance(node, ast.ClassDef):
                class_names.append(node.name)
    except SyntaxError:
        pass

    parts = [f"=== FILE: {path.name} ==="]
    if imports:
        parts.append("IMPORTS:\n" + "\n".join(imports[:20]))
    if func_names:
        parts.append("FUNCTIONS: " + ", ".join(func_names))
    if class_names:
        parts.append("CLASSES: " + ", ".join(class_names))
    parts.append("FIRST 30 LINES:\n" + first_30)

    return "\n".join(parts)


def _claude_analyze(file_summaries: str, api_key: str) -> dict | None:
    prompt = (
        "You are analyzing Python files to find the main agent function. "
        "Here are the files found:\n\n"
        f"{file_summaries}\n\n"
        "Identify the ONE function or class that represents the main agent — "
        "the one that receives a task/query and returns a result. "
        "Ignore utility functions, webhooks, rate limiters, scrapers, helpers.\n\n"
        "Return ONLY this JSON (double-quoted keys and strings), nothing else:\n"
        '{\n'
        '  "file": "filename.py",\n'
        '  "function": "function_name",\n'
        '  "is_async": true,\n'
        '  "input_type": "str",\n'
        '  "description": "one sentence what it does",\n'
        '  "capabilities": ["cap1", "cap2"],\n'
        '  "llm_provider": "anthropic"\n'
        '}'
    )

    try:
        with httpx.Client(timeout=30) as client:
            r = client.post(
                ANTHROPIC_API_URL,
                headers={
                    "x-api-key": api_key,
                    "anthropic-version": "2023-06-01",
                    "content-type": "application/json",
                },
                json={
                    "model": "claude-haiku-4-5-20251001",
                    "max_tokens": 500,
                    "messages": [{"role": "user", "content": prompt}],
                },
            )
        if r.status_code != 200:
            _err(f"Claude API error (HTTP {r.status_code}): {r.text[:200]}")
            return None

        text = r.json()["content"][0]["text"].strip()
        match = re.search(r"\{.*\}", text, re.DOTALL)
        if not match:
            _err("Claude returned no JSON object")
            return None
        return json.loads(match.group())
    except Exception as exc:
        _err(f"Claude API call failed: {exc}")
        return None


def phase3_analyze() -> dict:
    _phase(3, "Code analysis (read-only)")

    cwd = Path.cwd()
    py_files = [
        p for p in cwd.glob("*.py")
        if p.name not in ("install_eea_agent.py", "run_eea_agent.py")
    ]

    if not py_files:
        _info("No .py files found in current directory.")
        file_name = input("  Filename (e.g. my_agent.py): ").strip()
        symbol = input("  Function name (e.g. run_agent): ").strip()
        return {
            "file": file_name, "function": symbol,
            "is_async": False, "input_type": "str",
            "description": symbol.replace("_", " ").title(),
            "capabilities": ["information_retrieval"],
            "llm_provider": "none",
        }

    _info(f"Scanning {len(py_files)} file(s) in {cwd} ...")
    summaries = "\n\n".join(_build_file_summary(f) for f in py_files)

    api_key = os.environ.get("ANTHROPIC_API_KEY", "")
    if not api_key:
        _info("ANTHROPIC_API_KEY not set.")
        api_key = input("  Paste your Anthropic API key (or press Enter to skip): ").strip()

    result = None
    if api_key:
        _info("Analyzing code with Claude ...")
        result = _claude_analyze(summaries, api_key)

    if result is None:
        _info("Falling back to manual entry.")
        file_name = input("  Filename (e.g. my_agent.py): ").strip()
        symbol = input("  Function name (e.g. run_agent): ").strip()
        return {
            "file": file_name, "function": symbol,
            "is_async": False, "input_type": "str",
            "description": symbol.replace("_", " ").title(),
            "capabilities": ["information_retrieval"],
            "llm_provider": "none",
        }

    caps = [c for c in result.get("capabilities", []) if c in CAPABILITIES_LIST]
    result["capabilities"] = caps if caps else ["information_retrieval"]
    return result


# ─── Phase 4 — Confirm detection ─────────────────────────────────────────────

def phase4_confirm(detection: dict) -> dict:
    _phase(4, "Confirm detection")

    _ok(f"Detected agent: {detection['function']}() in {detection['file']}")
    _ok(f"Description: {detection.get('description', 'N/A')}")
    _ok(f"Capabilities: {', '.join(detection['capabilities'])}")
    _ok(f"LLM: {detection.get('llm_provider', 'none')}")

    ans = input("\n  Is this correct? [Y/n]: ").strip().lower()
    if ans in ("", "y", "yes"):
        return detection

    file_name = input("  Filename (e.g. my_agent.py): ").strip()
    symbol = input("  Function name (e.g. run_agent): ").strip()
    return {
        "file": file_name,
        "function": symbol,
        "is_async": False,
        "input_type": "str",
        "description": symbol.replace("_", " ").title(),
        "capabilities": ["information_retrieval"],
        "llm_provider": "none",
    }


# ─── Phase 5 — Configuration (automatic) ─────────────────────────────────────

def phase5_configure(detection: dict) -> dict:
    _phase(5, "EEA Configuration (automatic)")

    desc = detection.get("description", "")
    name = desc.title() if desc else detection["function"].replace("_", " ").title()
    name = name[:50]  # API enforces 50-char max
    _ok(f"Agent name: {name}")

    capabilities = detection["capabilities"]
    _ok(f"Capabilities: {', '.join(capabilities)}")

    tags = sorted({c.split("_")[0] for c in capabilities})
    _ok(f"Tags: {', '.join(tags)}")

    llm_provider: str | None = detection.get("llm_provider") or None
    if llm_provider in ("none", "None", ""):
        llm_provider = None
    _ok(f"LLM provider: {llm_provider or 'none'}")

    return {
        "name": name,
        "capabilities": capabilities,
        "min_price": 5.0,
        "tags": tags,
        "llm_provider": llm_provider,
        "webhook_port": 8001,
        "max_concurrent_tasks": 3,
    }


# ─── Phase 6 — Webhook setup ─────────────────────────────────────────────────

def _get_ngrok_url() -> str | None:
    try:
        r = httpx.get("http://localhost:4040/api/tunnels", timeout=5)
        tunnels = r.json().get("tunnels", [])
        for t in tunnels:
            if t.get("proto") == "https":
                return t["public_url"]
        for t in tunnels:
            return t.get("public_url")
    except Exception:
        return None


def phase6_webhook() -> str:
    _phase(6, "Webhook setup")

    existing = _get_ngrok_url()
    if existing:
        _ok(f"ngrok tunnel: {existing}")
        return existing.rstrip("/")

    home = os.path.expanduser("~")
    ngrok_candidates = [
        "ngrok",
        os.path.join(home, "AppData", "Local", "ngrok", "ngrok.exe"),
        os.path.join(home, "scoop", "shims", "ngrok.exe"),
        r"C:\ProgramData\chocolatey\bin\ngrok.exe",
    ]

    ngrok_bin = None
    for candidate in ngrok_candidates:
        try:
            result = subprocess.run(
                [candidate, "version"],
                capture_output=True, text=True, timeout=5,
            )
            if result.returncode == 0:
                ngrok_bin = candidate
                break
        except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
            continue

    if ngrok_bin:
        _info(f"Starting ngrok: {ngrok_bin}")
        subprocess.Popen(
            [ngrok_bin, "http", "8001"],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
        )
        time.sleep(4)
        public_url = _get_ngrok_url()
        if not public_url:
            _info("Waiting a bit longer for ngrok ...")
            time.sleep(3)
            public_url = _get_ngrok_url()
        if public_url:
            _ok(f"ngrok tunnel: {public_url}")
            return public_url.rstrip("/")
    else:
        print()
        print("  ngrok not found. Open a new terminal and run:")
        print("    ngrok http 8001")
        print()

    input("  Press Enter when ngrok is running: ")
    public_url = _get_ngrok_url()
    if public_url:
        _ok(f"ngrok tunnel: {public_url}")
        return public_url.rstrip("/")

    _err("Still could not read ngrok URL — using localhost fallback.")
    return "http://localhost:8001"


# ─── Phase 7 — Account ───────────────────────────────────────────────────────

def _supabase_signup(email: str, password: str) -> tuple[dict, int]:
    with httpx.Client(timeout=15) as client:
        r = client.post(
            f"{SUPABASE_URL}/auth/v1/signup",
            headers={"apikey": ANON_KEY, "Content-Type": "application/json"},
            json={"email": email, "password": password},
        )
    return r.json(), r.status_code


def _supabase_login(email: str, password: str) -> tuple[dict, int]:
    with httpx.Client(timeout=15) as client:
        r = client.post(
            f"{SUPABASE_URL}/auth/v1/token?grant_type=password",
            headers={"apikey": ANON_KEY, "Content-Type": "application/json"},
            json={"email": email, "password": password},
        )
    return r.json(), r.status_code


def _get_password_with_asterisks(prompt: str) -> str:
    print(prompt, end="", flush=True)
    password = ""
    while True:
        if sys.platform == "win32":
            import msvcrt
            ch = msvcrt.getwch()
            if ch in ("\r", "\n"):
                print()
                break
            elif ch == "\x08":  # backspace
                if password:
                    password = password[:-1]
                    print("\b \b", end="", flush=True)
            elif ch == "\x03":  # ctrl+c
                raise KeyboardInterrupt
            else:
                password += ch
                print("*", end="", flush=True)
        else:
            import tty, termios
            fd = sys.stdin.fileno()
            old = termios.tcgetattr(fd)
            try:
                tty.cbreak(fd)
                ch = sys.stdin.read(1)
                if ch in ("\r", "\n"):
                    print()
                    break
                elif ch == "\x7f":  # backspace
                    if password:
                        password = password[:-1]
                        print("\b \b", end="", flush=True)
                else:
                    password += ch
                    print("*", end="", flush=True)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old)
    return password


def phase7_account() -> dict:
    _phase(7, "Account")

    email = input("  Email: ").strip()
    if not email:
        _err("Email is required.")
        sys.exit(1)

    while True:
        password = _get_password_with_asterisks("  Password: ")
        if len(password) < 8:
            _err("Password must be at least 8 characters.")
            continue
        confirm = _get_password_with_asterisks("  Confirm password: ")
        if password != confirm:
            _err("Passwords do not match. Try again.")
            continue
        break

    _info("Creating account ...")
    try:
        data, status = _supabase_signup(email, password)

        if status in (200, 201):
            user = data.get("user") or {}
            _ok(f"Account created for {email}")
            return {
                "email": email,
                "user_id": user.get("id", ""),
                "access_token": data.get("access_token", ""),
            }

        msg = (
            data.get("msg") or data.get("message")
            or data.get("error_description") or ""
        ).lower()

        if "already registered" in msg or status == 422:
            _info("Account already exists — logging in ...")
            data2, status2 = _supabase_login(email, password)
            if status2 == 200 and "access_token" in data2:
                _ok(f"Logged in as {email}")
                return {
                    "email": email,
                    "user_id": (data2.get("user") or {}).get("id", ""),
                    "access_token": data2["access_token"],
                }
            _err(f"Login failed (HTTP {status2}): {data2}")
            sys.exit(1)

        _err(f"Signup error (HTTP {status}): {data}")
        sys.exit(1)

    except Exception as exc:
        _err(f"Request failed: {exc}")
        sys.exit(1)


# ─── Phase 8 — EEA Registration ──────────────────────────────────────────────

def _check_existing_runner() -> str | None:
    # Fast path: .eea_agent file written at registration time
    dot_file = Path.cwd() / ".eea_agent"
    if dot_file.exists():
        try:
            did = dot_file.read_text(encoding="utf-8").strip()
            if did:
                return did
        except OSError:
            pass

    # Fallback: grep DID comment from run_eea_agent.py
    runner = Path.cwd() / "run_eea_agent.py"
    if not runner.exists():
        return None
    try:
        text = runner.read_text(encoding="utf-8", errors="ignore")
        match = re.search(r"agent://eea/[^\s\"']+", text)
        if match:
            return match.group()
    except OSError:
        pass
    return None


def _check_supabase_existing(webhook_url: str) -> dict | None:
    wh = webhook_url.rstrip("/")
    if not wh.endswith("/webhook"):
        wh = wh + "/webhook"
    try:
        with httpx.Client(timeout=10) as client:
            r = client.get(
                f"{SUPABASE_URL}/rest/v1/user_agents",
                headers={
                    "apikey": SERVICE_KEY,
                    "Authorization": f"Bearer {SERVICE_KEY}",
                },
                params={"webhook_url": f"eq.{wh}", "limit": "1"},
            )
        if r.status_code == 200:
            rows = r.json()
            if rows:
                return rows[0]
    except Exception:
        pass
    return None


def phase8_register(config: dict, webhook_url: str) -> dict:
    _phase(8, "EEA Registration")

    # ── Check for existing registration ───────────────────────────────────
    did_from_runner = _check_existing_runner()
    if did_from_runner:
        print(f"\n  You already have an agent registered: {did_from_runner}")
        ans = input("  Register a new one anyway? [y/N]: ").strip().lower()
        if ans not in ("y", "yes"):
            _ok(f"Using existing agent: {did_from_runner}")
            return {"did": did_from_runner, "api_token": ""}

    existing = _check_supabase_existing(webhook_url)
    if existing:
        print(f"\n  Found existing agent for this webhook:")
        print(f"    Name:  {existing.get('name', '?')}")
        print(f"    DID:   {existing.get('did', '?')}")
        ans = input("  Register a new agent anyway? [y/N]: ").strip().lower()
        if ans not in ("y", "yes"):
            _ok(f"Using existing agent: {existing.get('did', '')}")
            return {
                "did": existing.get("did", ""),
                "api_token": existing.get("api_token", ""),
            }

    # Normalise fields — guard against upstream type drift
    name = str(config["name"])[:50]
    capabilities = [c for c in config["capabilities"] if c] or ["information_retrieval"]
    tags = sorted(set(config["tags"])) if config["tags"] else []
    llm_provider = config["llm_provider"]
    if llm_provider in ("none", "None", "", "null"):
        llm_provider = None
    wh_url = webhook_url.rstrip("/")
    if not wh_url.endswith("/webhook"):
        wh_url = wh_url + "/webhook"

    payload = {
        "name": name,
        "capabilities": capabilities,
        "min_price_joules": float(config["min_price"]),
        "tags": tags,
        "webhook_url": wh_url,
        "llm_provider": llm_provider,
        "max_concurrent_tasks": int(config.get("max_concurrent_tasks", 3)),
    }

    while True:
        try:
            _info("Registering agent ...")
            with httpx.Client(timeout=30.0) as client:
                r = client.post(f"{EEA_URL}/registry/register", json=payload)

            if r.status_code in (200, 201):
                data = r.json()
                did = data.get("did") or data.get("agent_did", "")
                api_token = data.get("api_token") or data.get("token", "")
                _ok(f"Registered — DID: {did}")

                try:
                    _info("Completing onboarding ...")
                    with httpx.Client(timeout=15) as client2:
                        ob = client2.post(
                            f"{EEA_URL}/onboarding/submit",
                            json={"agent_did": did, "answers": {"q1": "No", "q2": "980 JOULE"}},
                        )
                    if ob.status_code < 300:
                        _ok("Onboarding complete")
                    else:
                        _info(f"Onboarding note (HTTP {ob.status_code}): {ob.text[:200]}")
                except Exception as exc:
                    _info(f"Onboarding skipped: {exc}")

                try:
                    Path(".eea_agent").write_text(did, encoding="utf-8")
                except OSError:
                    pass
                return {"did": did, "api_token": api_token}

            _err(f"Registration failed (HTTP {r.status_code}): {r.text[:300]}")

        except Exception as exc:
            _err(f"Request failed: {exc}")

        if not _ask_retry("registration"):
            sys.exit(1)


# ─── Phase 9 — Generate adapter file ─────────────────────────────────────────

# Double-brace {{ }} escapes literal braces in .format() template.
RUNNER_TEMPLATE = '''\
# EEA Agent DID: {did}
"""
EEA Agent Runner -- generated by EEA Facilitator
Run: python run_eea_agent.py
"""
import asyncio
import sys

sys.path.insert(0, r"C:\\eea")

from adapter.eea_adapter import EEAAdapter, BALANCED
from {module} import {fn}


async def agent_wrapper(prompt: str):
    import inspect
    fn = {fn}
    sig = inspect.signature(fn)
    params = list(sig.parameters.values())
    first_param = params[0] if params else None

    if first_param is not None and (
        first_param.annotation == list
        or "list" in str(first_param.annotation).lower()
    ):
        args = [[prompt]]
    else:
        args = [prompt]

    if asyncio.iscoroutinefunction(fn):
        result = await fn(*args)
    else:
        result = fn(*args)

    if isinstance(result, tuple):
        result = result[0]
    if isinstance(result, str):
        return {{"output": result}}
    return result if isinstance(result, dict) else {{"output": str(result)}}


adapter = EEAAdapter(
    agent_fn=agent_wrapper,
    eea_url="https://api.agenesis.io",
    name={name!r},
    capabilities={capabilities!r},
    min_price_joules=5.0,
    tags={tags!r},
    webhook_port=8001,
    public_webhook_url={webhook_url!r},
)

if __name__ == "__main__":
    asyncio.run(adapter.run())
'''


def phase9_generate(detection: dict, config: dict, webhook_url: str, did: str = "") -> Path:
    _phase(9, "Generate adapter file")

    module = Path(detection["file"]).stem
    runner_path = Path.cwd() / "run_eea_agent.py"

    content = RUNNER_TEMPLATE.format(
        did=did,
        module=module,
        fn=detection["function"],
        name=config["name"],
        capabilities=config["capabilities"],
        tags=config["tags"],
        webhook_url=webhook_url,
    )

    runner_path.write_text(content, encoding="utf-8")
    _ok(f"Created {runner_path}")
    return runner_path


# ─── Phase 10 — Validate by running adapter ──────────────────────────────────

def phase10_validate(runner_path: Path) -> None:
    _phase(10, "Validate adapter")

    _info(f"Starting {runner_path.name} as subprocess (10 second test) ...")
    try:
        proc = subprocess.Popen(
            [sys.executable, str(runner_path)],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        time.sleep(10)
        ret = proc.poll()

        if ret is None:
            _ok("Adapter started successfully (still running after 10s)")
            proc.terminate()
            try:
                proc.wait(timeout=5)
            except subprocess.TimeoutExpired:
                proc.kill()
        else:
            stderr_out = b""
            if proc.stderr:
                stderr_out = proc.stderr.read()
            _err(f"Adapter exited immediately (code {ret})")
            if stderr_out:
                _info(f"Error output: {stderr_out.decode('utf-8', errors='ignore')[:400]}")
            _info("You can still run it manually: python run_eea_agent.py")

    except Exception as exc:
        _err(f"Could not start adapter: {exc}")
        _info("You can still run it manually: python run_eea_agent.py")


# ─── Phase 11 — Save to Supabase + Final summary ─────────────────────────────

def phase11_finish(account: dict, config: dict, reg: dict, webhook_url: str) -> None:
    _phase(11, "Save + Summary")

    row = {
        "user_id": account["user_id"],
        "did": reg["did"],
        "name": config["name"],
        "api_token": reg["api_token"],
        "capabilities": config["capabilities"],
        "min_price_joules": config["min_price"],
        "tags": config["tags"],
        "llm_provider": config["llm_provider"],
        "webhook_url": webhook_url,
    }

    try:
        with httpx.Client(timeout=15) as client:
            r = client.post(
                f"{SUPABASE_URL}/rest/v1/user_agents",
                headers={
                    "apikey": SERVICE_KEY,
                    "Authorization": f"Bearer {SERVICE_KEY}",
                    "Content-Type": "application/json",
                    "Prefer": "return=minimal",
                },
                json=row,
            )
        if r.status_code in (200, 201, 204):
            _ok("Agent record saved to Supabase")
        else:
            _info(f"Supabase note (HTTP {r.status_code}): {r.text[:200]}")
    except Exception as exc:
        _info(f"Could not save to Supabase (non-fatal): {exc}")

    did = reg.get("did") or "pending"
    did_display = (did[:36] + "...") if len(did) > 39 else did.ljust(39)

    print(f"""
+--------------------------------------------------+
|  ✓ Your agent is connected to EEA!              |
|                                                  |
|  DID:     {did_display}  |
|  Balance: 50,000 JOULE                           |
|  Status:  active                                 |
|                                                  |
|  To run your agent:                              |
|    python run_eea_agent.py                       |
|                                                  |
|  Dashboard: https://agenesis.io/dashboard        |
+--------------------------------------------------+
""")

    try:
        webbrowser.open("https://agenesis.io/dashboard")
    except Exception:
        pass


# ─── Entrypoint ───────────────────────────────────────────────────────────────

def main() -> None:
    try:
        phase1_banner()
        phase2_environment()
        detection = phase3_analyze()
        detection = phase4_confirm(detection)
        config = phase5_configure(detection)
        webhook_url = phase6_webhook()

        # Early-exit if agent already registered — skip login entirely
        existing_did = _check_existing_runner()
        if not existing_did:
            existing_row = _check_supabase_existing(webhook_url)
            if existing_row:
                existing_did = existing_row.get("did", "")

        if existing_did:
            print(f"\n  You already have an agent registered: {existing_did}")
            ans = input("  Register a new one anyway? [y/N]: ").strip().lower()
            if ans not in ("y", "yes"):
                _ok(f"Using existing agent: {existing_did}")
                api_token = ""
                if existing_row := _check_supabase_existing(webhook_url):
                    api_token = existing_row.get("api_token", "")
                reg = {"did": existing_did, "api_token": api_token}
                runner_path = phase9_generate(detection, config, webhook_url, did=existing_did)
                phase10_validate(runner_path)
                phase11_finish({"email": "", "user_id": ""}, config, reg, webhook_url)
                return

        account = phase7_account()
        reg = phase8_register(config, webhook_url)
        runner_path = phase9_generate(detection, config, webhook_url, did=reg.get("did", ""))
        phase10_validate(runner_path)
        phase11_finish(account, config, reg, webhook_url)
    except KeyboardInterrupt:
        print("\n\n  Interrupted. Run the script again to resume.")
        sys.exit(0)


if __name__ == "__main__":
    main()
