"""Time bounded file-only observation of a user flown jump. Never sends game/process/input commands."""
import argparse, datetime as dt, json, pathlib, time

p=argparse.ArgumentParser()
p.add_argument('--journal',type=pathlib.Path,required=True)
p.add_argument('--output',type=pathlib.Path,required=True)
p.add_argument('--duration',type=float,default=180)
a=p.parse_args();a.output.mkdir(parents=True,exist_ok=True)
started=dt.datetime.now(dt.timezone.utc);end=time.monotonic()+min(600,max(1,a.duration));seen=set();snapshots={};counts={}
events={'FSDTarget','StartJump','FSDJump','FuelScoop','HeatWarning','HeatDamage','SupercruiseEntry','SupercruiseExit','NavRouteClear','Shutdown','Location','Loadout'}
allowed={'timestamp','event','JumpType','StarSystem','SystemAddress','StarPos','StarClass','Body','BodyID','BodyType','Name','RemainingJumpsInRoute','FuelUsed','FuelLevel','JumpDist','BoostUsed','Total','Scooped','Heat','Ship','ShipID','FuelCapacity','UnladenMass','MaxJumpRange','Modules','Docked','MarketID'}
output=a.output/'observations.jsonl'
def emit(kind,value):
    with output.open('a',encoding='utf-8') as f:f.write(json.dumps({'observedAt':dt.datetime.now(dt.timezone.utc).isoformat(),'kind':kind,'value':value},ensure_ascii=False)+'\n')
    counts[kind]=counts.get(kind,0)+1

while time.monotonic()<end and not (a.output/'stop.request').exists():
    logs=sorted(a.journal.glob('Journal*.log'),key=lambda x:x.stat().st_mtime,reverse=True)[:2]
    for log in logs:
        try:
            # Journals are append only. The short observation includes recent pre-roll events, with complete UTF-8 lines only.
            with log.open('r',encoding='utf-8-sig') as f:
                for index,line in enumerate(f):
                    key=(str(log),index)
                    if key in seen or not line.endswith('\n'):continue
                    seen.add(key)
                    try:
                        e=json.loads(line);at=dt.datetime.fromisoformat(e.get('timestamp','').replace('Z','+00:00'))
                        if e.get('event') in events and (started-at).total_seconds()<180:emit('journal',{k:v for k,v in e.items() if k in allowed})
                    except (ValueError,TypeError):pass
        except OSError:pass
    for name in ['Status.json','NavRoute.json','ModulesInfo.json']:
        path=a.journal/name
        try:
            text=path.read_text(encoding='utf-8-sig')
            if snapshots.get(name)==text:continue
            value=json.loads(text);snapshots[name]=text;emit(name,value)
        except (OSError,ValueError):pass
    time.sleep(.1)

report={'scope':'file-only manual flight observation','startedAt':started.isoformat(),'finishedAt':dt.datetime.now(dt.timezone.utc).isoformat(),'counts':counts,'gameInputsSent':0,'processWrites':0,'gameExitInvoked':False,'heatPercentAvailableFromStatus':False}
(a.output/'result.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps(report,ensure_ascii=False))
