"""Close the existing docking availability leaf by its actual typed slot."""
import hashlib
import importlib.util
import json
import struct
from pathlib import Path

spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
audit=importlib.util.module_from_spec(spec);spec.loader.exec_module(audit)
old=audit.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=audit.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
hashes=[hashlib.sha256(i.raw).hexdigest() for i in (old,new)]
if hashes!=['6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93','ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63']:
    raise ValueError('Pinned docking-binding image changed')
before=0xE8C6E0;span=old.function(before)
if span is None or span[0]!=before:raise ValueError('Original docking ctor lost its exact function range')
length=span[1]-before;ctor=audit.match(old,new,before,length)
if ctor['status']!='unique':raise ValueError('Original docking constructor is not uniquely bound')
ctor['oldRva']=hex(before);references=audit.references(old,new,before,int(ctor['newRva'],16),length)
table=0x5165288;targets={r[1] for r in references if r[0]==table}
if len(targets)!=1:raise ValueError('Typed docking interface has no unique paired ctor operand')
new_table=targets.pop()
if new_table!=0x5165248:raise ValueError('Current source docking table disagrees with actual ctor')
slots=[]
for offset,expected_new in ((0,None),(0x30,0xED7D10),(0x58,0xED7E10),(0x88,0xEF8A60)):
    a=struct.unpack('<Q',old.data(table+offset,8))[0]-old.base
    b=struct.unpack('<Q',new.data(new_table+offset,8))[0]-new.base
    if expected_new is not None and b!=expected_new:raise ValueError('Actual current typed docking slot changed')
    span=old.function(a)
    size=span[1]-a if span and span[0]==a else next(i.address+i.size-old.base-a for i in old.instructions(a,128) if i.mnemonic=='ret')
    raw,mask,*_=audit.signature(old,a,size);actual=new.data(b,len(raw))
    if any(x!=y for x,y,keep in zip(raw,actual,mask) if keep):raise ValueError('Typed docking slot body changed')
    refs=audit.references(old,new,a,b,len(raw));references.extend(refs)
    slots.append(dict(slot=hex(offset),oldVtable=hex(table),newVtable=hex(new_table),oldRva=hex(a),newRva=hex(b),bytes=len(raw),
        normalizedIdentical=True,oldSha256=hashlib.sha256(raw).hexdigest(),newSha256=hashlib.sha256(actual).hexdigest(),references=[list(map(hex,r)) for r in refs]))
availability=next(r for r in slots if r['slot']=='0x58')
instructions=old.instructions(int(availability['oldRva'],16),availability['bytes'])
if [(i.mnemonic,i.op_str) for i in instructions]!=[('movzx','eax, byte ptr [rcx + 0x377]'),('ret','')]:
    raise ValueError('Docking availability is no longer its original pure published-byte getter')
type_globals={r[1] for r in references if r[0]==0x5F5317C}
if type_globals!={0x5F5317C}:raise ValueError('Typed docking registration global is unbound')
result=dict(runtimeNeutral=True,canInstallHooks=False,runtimeValidated=False,gameCalls=0,networkRequests=0,
    oldImageSha256=hashes[0],newImageSha256=hashes[1],functions=[ctor],slots=slots,
    vtables={hex(table):hex(new_table)},globals={'0x5f5317c':'0x5f5317c'},references=[list(map(hex,r)) for r in references],
    availability=dict(oldRva=availability['oldRva'],newRva=availability['newRva'],field='interface+377',
        actualTypedSlot='58',pureRead=True,gameMethodInvoked=False),
    limitations=['Static typed identity only; does not authorize hook installation or prove runtime readiness.',
        'Navigation.LocalFlight.cs is unchanged. No generic cruise-assist getter is substituted.'])
path=Path('artifacts/galaxy-client-compatibility/local-docking-slot-closure.json')
path.write_text(json.dumps(result,indent=2)+'\n',encoding='utf8')
print(json.dumps(dict(report=str(path),vtables=result['vtables'],slots=slots,availability=result['availability']),indent=2))
