#!/usr/bin/env python3
"""HTTP end-to-end test: installs a disposable copy; never modifies the actual release package."""
from pathlib import Path
import os, tempfile, subprocess, shutil, socket, time, urllib.request, urllib.error, urllib.parse, json, re, sys
src=Path(__file__).resolve().parents[1]
checks=0

def ok(cond,name):
    global checks
    if not cond: raise AssertionError(name)
    checks+=1
    print('PASS',name)

def req(url, data=None, token=None, content_type='application/json'):
    headers={}
    if token:headers['Authorization']='Bearer '+token
    if data is not None:
        data=(json.dumps(data).encode('utf-8') if content_type=='application/json' else urllib.parse.urlencode(data).encode())
        headers['Content-Type']=content_type
    request=urllib.request.Request(url,data,headers=headers)
    try:
        with urllib.request.urlopen(request,timeout=10) as r: return r.status,r.read().decode()
    except urllib.error.HTTPError as e: return e.code,e.read().decode()

with tempfile.TemporaryDirectory(prefix='clearai-brain-http-') as temp:
    root=Path(temp)/'service';shutil.copytree(src,root,ignore=shutil.ignore_patterns('config.php','database.json','*.lock','*.tmp'))
    with socket.socket() as sock:sock.bind(('127.0.0.1',0));port=sock.getsockname()[1]
    server=subprocess.Popen(['php','-S',f'127.0.0.1:{port}','-t',str(root/'public')],stdout=subprocess.DEVNULL,stderr=subprocess.PIPE)
    base=f'http://127.0.0.1:{port}'
    try:
        for _ in range(30):
            try: status,_=req(base+'/index.php');break
            except Exception:time.sleep(.1)
        ok(status==200,'status page serves over localhost')
        status,b=req(base+'/api.php?route=/v1/health');d=json.loads(b)
        ok(status==200 and not d['installed'] and d['version']=='0.1.0','health shows uninstalled')
        status,_=req(base+'/install.php',{'setup_code':'this is not the code'},content_type='application/x-www-form-urlencoded')
        ok(status==200 and not (root/'private/config.php').exists(),'wrong setup code rejected')
        code=(root/'private/setup-code.txt').read_text().strip()
        status,b=req(base+'/install.php',{'setup_code':code},content_type='application/x-www-form-urlencoded')
        ok(status==200 and 'Installation complete' in b,'one-time installer completes')
        token=re.search(r'<pre>([a-f0-9]{64})</pre>',b).group(1)
        ok(not (root/'private/setup-code.txt').exists() and token not in (root/'private/config.php').read_text(),'setup code removed and raw API token not stored')
        ok(req(base+'/install.php')[0]==403,'installer locked after install')
        config=root/'private/config.php'
        # HTTPS is mandatory in production; bypass only in disposable loopback test.
        script='''<?php $p=$argv[1];$c=require $p;$c['require_https']=false;file_put_contents($p,"<?php\\nreturn ".var_export($c,true).";\\n");'''
        subprocess.check_call(['php','-r',script.removeprefix('<?php '),str(config)],stdout=subprocess.DEVNULL)
        url=base+'/api.php?route='
        status,b=req(url+'/v1/memory/search',{'query':'PHP'})
        ok(status==401,'requests without token denied')
        status,b=req(url+'/v1/memory/save',{'user_id':'owner','text':'I prefer PHP','title':'Preference'},token)
        memory=json.loads(b)['data'];ok(status==200 and memory['title']=='Preference','authenticated save')
        status,b=req(url+'/v1/memory/search',{'user_id':'owner','query':'PHP'},token)
        ok(status==200 and len(json.loads(b)['data']['items'])==1,'authenticated query')
        status,b=req(url+'/v1/memory/search',{'user_id':'someone-else','query':'PHP'},token)
        ok(status==200 and len(json.loads(b)['data']['items'])==0,'user isolation over HTTP')
        status,b=req(url+'/v1/memory/search',{'user_id':'../../etc/passwd','query':'PHP'},token)
        ok(status==422,'malformed identity rejected')
        status,b=req(url+'/v1/agent/create',{'goal':'Calculate 3*(4+5)'},token)
        j=json.loads(b)['data'];ok(status==200 and j['status']=='queued','agent plan created')
        status,b=req(url+'/v1/agent/step',{'id':j['id']},token)
        run=json.loads(b)['data'];ok(status==200 and run['status']=='completed' and run['steps'][0]['result']['value']==27,'agent actually executes calculation')
        status,b=req(url+'/v1/agent/create',{'goal':'Can you deploy a new website and pay a bill?'},token)
        ok(status==422 and 'Supply explicit steps' in b,'unsupported autonomy clearly rejected')
        status,b=req(url+'/v1/brain/route',{},token)
        ok(status==200 and not json.loads(b)['data']['configured'],'unconfigured model declared')
        status,b=req(url+'/v1/brain/respond',{'message':'Hello'},token)
        ok(status==503 and 'No model configured' in b,'unconfigured model returns error, never fake response')
        status,b=req(url+'/v1/audit/list',{},token)
        ok(status==200 and len(json.loads(b)['data']['items'])>=2,'audit trail available')
        status,b=req(base+'/private/config.php')
        ok('token_hash' not in b and '<?php' not in b and token not in b,'private config never served to HTTP client')
        print(f'ALL {checks} HTTP checks passed')
    finally:
        server.terminate()
        try:server.communicate(timeout=4)
        except subprocess.TimeoutExpired:server.kill()
