import json,time,threading,sys
from http.server import ThreadingHTTPServer,BaseHTTPRequestHandler
from pathlib import Path
root=Path(__file__).parent
lock=threading.Lock()
def node(repo,i):
 return dict(iid=str(i),title=f'Synthetic change {i:03d} for {repo.split("/")[-1]}',webUrl=f'https://gitlab.com/{repo}/-/merge_requests/{i}',createdAt='2026-07-01T00:00:00Z',updatedAt=f'2026-07-{30-(i//24):02d}T{23-(i%24):02d}:00:00Z',sourceBranch=f'change-{i}',targetBranch='main',state='opened',draft=False,author={'username':'demo-author','name':'Demo Author'},approved=True,approvedBy={'nodes':[]})
class Handler(BaseHTTPRequestHandler):
 def log_message(self,*a): pass
 def do_GET(self): self.handle_request()
 def do_POST(self): self.handle_request()
 def handle_request(self):
  raw=b''
  if self.headers.get('Transfer-Encoding','').lower()=='chunked':
   while True:
    n=int(self.rfile.readline().strip(),16)
    if n==0:self.rfile.readline();break
    raw+=self.rfile.read(n);self.rfile.read(2)
  else:raw=self.rfile.read(int(self.headers.get('content-length',0)))
  try: body=json.loads(raw or '{}')
  except: body={}
  q=body.get('query','');v=body.get('variables',{});status=200;kind='other';data={}
  if self.path=='/control':
   (root/'scenario.json').write_text(json.dumps(body));data={'ok':True}
  elif 'graphql' in self.path:
   if 'metadata { version' in q: kind='version';data={'data':{'metadata':{'version':'18.5.0'}}}
   elif 'currentUser' in q: kind='viewer';data={'data':{'currentUser':{'username':'demo-viewer','name':'Demo Viewer'}}}
   else:
    repos=v.get('fullPaths') or [v.get('fullPath','demo/project-001')];kind='batch' if 'fullPaths' in v else ('details' if 'iids' in v else 'single')
    scenario=json.loads((root/'scenario.json').read_text()) if (root/'scenario.json').exists() else {}
    if kind=='batch' and scenario.get('fail') and any(r.endswith('011') for r in repos): status=503;data={'message':'Injected batch failure'}
    elif kind=='single' and scenario.get('failSingle') and any(r.endswith('011') for r in repos):status=503;data={'message':'Injected project failure'}
    else:
     start=int(v.get('after') or 0);count=int(v.get('first',100))
     def page(r):
      total=(120 if r.endswith('001') else 2) if not (v.get('authorUsername') or v.get('reviewerUsername')) else 0;end=min(start+count,total)
      return {'pageInfo':{'hasNextPage':end<total,'endCursor':str(end) if end<total else None},'nodes':[node(r,i) for i in range(start+1,end+1)]}
     data={'data':{'metadata':{'enterprise':True},**({'projects':{'nodes':[{'fullPath':r,'mergeRequests':page(r)} for r in repos]}} if kind=='batch' else {'project':{'mergeRequests':page(repos[0])}})}}
    time.sleep(.15)
  elif 'project-011' in self.path and json.loads((root/'scenario.json').read_text()).get('failSingle'):status=503;kind='rest-failure';data={'message':'Injected project failure'}
  elif '/user' in self.path:kind='auth';data={'id':1,'username':'demo-viewer','name':'Demo Viewer'}
  elif '/version' in self.path:kind='rest-version';data={'version':'18.5.0'}
  else: data=[]
  record={'run':json.loads((root/'scenario.json').read_text()).get('run','setup'),'time':round(time.time(),3),'kind':kind,'status':status,'variables':v,'path':self.path}
  with lock:
   with (root/'requests.jsonl').open('a') as f:f.write(json.dumps(record)+'\n')
  payload=json.dumps(data).encode();self.send_response(status);self.send_header('Content-Type','application/json');self.send_header('Content-Length',str(len(payload)));self.end_headers();self.wfile.write(payload)
ThreadingHTTPServer(('127.0.0.1',18965),Handler).serve_forever()
