#!/usr/bin/env python3
"""Deterministic stdio Codex app-server: two web searches, one completion omitted.
Prompt chooses completed / failed / interrupted / stop. Stop waits for turn/interrupt.
No model, network, credentials, or database writes.
"""
import json, sys, time, uuid, threading, os
if '--version' in sys.argv:
    print('codex-cli 0.156.1'); sys.exit()
if '--help' in sys.argv:
    print('Deterministic Codex app-server fixture'); sys.exit()
lock=threading.Lock()
thread_id='fixture-'+str(uuid.uuid4()); turn_id=''; mode=''; cwd=os.getcwd(); now=int(time.time())
def send(value):
    with lock: print(json.dumps(value), flush=True)
def notify(method, params): send({'method':method,'params':params})
def turn(status, tid=None):
    return dict(id=tid or turn_id,items=[],itemsView='notLoaded',status=status,error=({'message':'Synthetic terminal failure','codexErrorInfo':'other','additionalDetails':None} if status=='failed' else None),startedAt=now,completedAt=(None if status=='inProgress' else int(time.time())),durationMs=None)
def thread():
    return dict(id=thread_id,sessionId=thread_id,projectId=None,forkedFromId=None,preview='',ephemeral=False,modelProvider='openai',createdAt=now,updatedAt=now,status={'type':'idle'},path='/synthetic/thread.jsonl',cwd=cwd,cliVersion='0.156.1',source='vscode',threadSource=None,agentNickname=None,agentRole=None,gitInfo=None,name=None,turns=[])
def finish(status, th=None, tid=None):
    notify('turn/completed',dict(threadId=th or thread_id,turn=turn(status,tid)))
def play(thread_id, turn_id, mode):
    time.sleep(1)
    notify('turn/started',dict(threadId=thread_id,turn=turn('inProgress',turn_id)))
    for ident, query in [('completed-control','completed control query'),('unfinished-search','unfinished search query')]:
        item=dict(type='webSearch',id=turn_id+'-'+ident,query=query,action=None)
        notify('item/started',dict(threadId=thread_id,turnId=turn_id,item=item,startedAtMs=now*1000,completedAtMs=int(time.time()*1000)))
        time.sleep(1)
        if ident=='completed-control': notify('item/completed',dict(threadId=thread_id,turnId=turn_id,item=item,startedAtMs=now*1000,completedAtMs=int(time.time()*1000)))
    if mode!='stop':
        time.sleep(3)
        finish(mode,thread_id,turn_id)
for line in sys.stdin:
    try:
        req=json.loads(line); method=req.get('method'); params=req.get('params') or {}; result={}
        if 'id' not in req: continue
        if method=='initialize': result=dict(userAgent='T3 Code/0.156.1',codexHome='/synthetic/codex',platformFamily='unix',platformOs='linux')
        elif method=='account/read': result=dict(account={'type':'apiKey'},requiresOpenaiAuth=False)
        elif method=='model/list': result=dict(data=[dict(id='gpt-5.4',model='gpt-5.4',displayName='Codex fixture',description='Deterministic search settlement',hidden=False,isDefault=True,supportedReasoningEfforts=[dict(reasoningEffort='low',description='Low')],defaultReasoningEffort='low',inputModalities=['text'],supportsPersonality=False)],nextCursor=None)
        elif method=='skills/list': result={'data':[]}
        elif method=='config/read': result={'config':{},'origins':{}}
        elif method in ('thread/start','thread/resume'):
            thread_id=params.get('threadId') or 'fixture-'+str(uuid.uuid4()); cwd=params.get('cwd',cwd)
            result=dict(thread=thread(),model='gpt-5.4',modelProvider='openai',serviceTier=None,cwd=cwd,instructionSources=[],approvalPolicy='never',approvalsReviewer='user',sandbox={'type':'dangerFullAccess'},reasoningEffort='low')
        elif method=='turn/start':
            thread_id=params['threadId']; text=' '.join(x.get('text','') for x in params.get('input',[])).lower()
            mode=next((x for x in ['stop','interrupted','failed'] if x in text),'completed'); turn_id='turn-'+str(uuid.uuid4()); now=int(time.time()); result={'turn':turn('inProgress')}
        elif method=='thread/read': result={'thread':thread()}
        elif method in ('account/rateLimits/read',):
            result={'rateLimits':{'primary':None,'secondary':None,'credits':None,'planType':None},'rateLimitsByLimitId':None,'rateLimitResetCredits':None,'accountId':None}
        send({'id':req['id'],'result':result})
        if method=='turn/start': threading.Thread(target=play,args=(thread_id,turn_id,mode),daemon=True).start()
        if method=='turn/interrupt': finish('interrupted',params['threadId'],params['turnId'])
    except Exception as e: print(str(e),file=sys.stderr,flush=True)
