"""Real Electron main against one multiplexing gateway (default + warm + cold): Desktop attach
for every profile through the production ensure/ticket code, then archive/unarchive of a
COLD profile's session over the exact REST shape the renderer sends, while the other two
profiles hold live attachments. Companion to run.py (single-profile ticket probe)."""
from __future__ import annotations

import argparse
import json
import logging
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import time

REPO = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(REPO))
from evals.desktop_gateway_ticket_native.run import stop_owned
from hermes_cli.gateway_runtime_discovery import query_identify

logger = logging.getLogger(__name__)


def ready(process, home, wanted):
    deadline = time.monotonic() + 90
    last = None
    while time.monotonic() < deadline and process.poll() is None:
        try:
            endpoint = query_identify(home, timeout=2)
            served = {Path(item['home']).resolve() for item in endpoint.get('served_profiles', [])}
            if endpoint.get('state') == 'ready' and endpoint.get('api_origin') and wanted <= served:
                return endpoint
        except (OSError, ValueError) as error:
            last = type(error).__name__
        time.sleep(0.2)
    raise RuntimeError(f'Multiplexer not ready: exit={process.poll()}, last={last}')


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--python', default=sys.executable)
    parser.add_argument('--electron', help='Native Electron binary, not electron/index.js')
    parser.add_argument('--output', type=Path, required=True)
    args = parser.parse_args()
    python = os.path.abspath(args.python)
    from hermes_constants import find_node_executable
    node = find_node_executable('node')
    if not node:
        parser.error('node is required')
    electron = args.electron or subprocess.check_output(
        [node, '-e', 'console.log(require("electron"))'], cwd=REPO / 'apps/desktop',
        stdin=subprocess.DEVNULL, text=True, encoding='utf-8', errors='replace', timeout=15).strip()
    output = args.output.resolve()
    output.mkdir(parents=True, exist_ok=True)
    receipt = {'passed': False, 'host': sys.platform}
    processes, logs = [], []
    temporary_directory = tempfile.TemporaryDirectory(prefix='h-mux-')
    try:
        base = Path(temporary_directory.name).resolve()
        home, state = base / 'home', base / 'state'
        profiles = {name: state / 'profiles' / name for name in ('warm', 'cold')}
        for directory in (home, state, *profiles.values(), base / 'user-data', base / 'tmp'):
            directory.mkdir(parents=True, mode=0o700, exist_ok=True)
        env = {key: value for key, value in os.environ.items() if key.upper() in {
            'PATH', 'DISPLAY', 'WAYLAND_DISPLAY', 'XDG_RUNTIME_DIR', 'XAUTHORITY', 'LD_LIBRARY_PATH', 'LANG', 'TZ'}}
        env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state),
                   XDG_CONFIG_HOME=str(home / '.config'), XDG_CACHE_HOME=str(home / '.cache'),
                   TEMP=str(base / 'tmp'), TMP=str(base / 'tmp'), TMPDIR=str(base / 'tmp'),
                   PYTHONPATH=str(REPO), PYTHONUNBUFFERED='1', PYTHONUTF8='1', PYTHONIOENCODING='utf-8')
        (state / 'config.yaml').write_text(json.dumps({
            'gateway': {'multiplex_profiles': True},
            'model': {'provider': 'custom', 'default': 'launch-model', 'base_url': 'http://127.0.0.1:1/v1'},
            'auxiliary': {'title_generation': {'enabled': False}}}), encoding='utf-8')
        for name, profile in profiles.items():
            (profile / 'config.yaml').write_text(json.dumps({'model': {
                'provider': 'custom', 'default': name + '-model', 'base_url': 'http://127.0.0.1:1/v1'}}), encoding='utf-8')
        log = (output / 'multiplexer.log').open('w', encoding='utf-8')
        logs.append(log)
        process = subprocess.Popen([python, '-m', 'gateway.run'], cwd=REPO, env=env,
                                   stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT)
        processes.append(process)
        identity = ready(process, state, {state.resolve(), *(p.resolve() for p in profiles.values())})
        bundle = base / 'electron-main-multiplex.cjs'
        subprocess.run([node, str(Path(__file__).with_name('build.mjs')), str(bundle),
                        str(Path(__file__).with_name('electron-main-multiplex.ts'))],
                       cwd=REPO, stdin=subprocess.DEVNULL, check=True, timeout=60)
        input_file = base / 'input.json'
        input_file.write_text(json.dumps({
            'repo': str(REPO), 'python': python, 'home': str(home), 'state': str(state),
            'identity': {k: identity.get(k) for k in ('instance_id', 'api_origin', 'served_profiles')},
            'userData': str(base / 'user-data'), 'receipt': str(output / 'receipt.json')}), encoding='utf-8')
        electron_log = (output / 'electron.log').open('w', encoding='utf-8')
        logs.append(electron_log)
        flags = ['--ozone-platform=headless', '--no-sandbox'] if sys.platform == 'linux' else []
        native = subprocess.Popen([electron, *flags, str(bundle)], cwd=REPO,
                                  env={**env, 'NATIVE_MUX_INPUT': str(input_file)},
                                  stdin=subprocess.DEVNULL, stdout=electron_log, stderr=subprocess.STDOUT)
        processes.append(native)
        status = native.wait(timeout=180)
        receipt = json.loads((output / 'receipt.json').read_text(encoding='utf-8'))
        if status or not receipt.get('passed'):
            raise RuntimeError(f'Electron probe failed: exit={status}')
    except Exception as error:
        logger.exception('harness error')
        receipt['passed'] = False
        receipt['harnessError'] = str(error)
    finally:
        for process in reversed(processes):
            stop_owned(process)
        for log in logs:
            log.close()
        temporary_directory.cleanup()
        receipt['ownedProcessesStopped'] = all(process.poll() is not None for process in processes)
        (output / 'receipt.json').write_text(json.dumps(receipt, indent=2) + '\n', encoding='utf-8')
    print(json.dumps(receipt, indent=2))
    return 0 if receipt['passed'] else 1


if __name__ == '__main__':
    raise SystemExit(main())
