"""Offline, allowlisted source extraction; never opens a process or game asset."""
from pathlib import Path
from datetime import datetime, timezone
import hashlib
import importlib.util
import json

ROOT = Path(__file__).resolve().parents[2]
OUT = Path(__file__).resolve().parent


def main():
    parser = ROOT / 'analysis/arbeit-catalog/followup/inspect_ui_metadata.py'
    spec = importlib.util.spec_from_file_location('_life_metadata', parser)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    module.META = parser.parent / 'client-copy/global-metadata.dat'
    metadata = module.Metadata()
    names = {'DetailCardUIInstance', 'DetailCardUIState', 'GatherableSheetIdCardUIInfo',
             'FisheryCardUIInfo', 'GatherableSheetId', 'FisherySheetId'}
    types = []
    for index, row in enumerate(metadata.types):
        if metadata.name(row[0]) not in names:
            continue
        types.append({'name': metadata.name(row[0]), 'namespace': metadata.name(row[1]),
                      'definition_index': index, 'flags': row[7],
                      'fields': [{'name': metadata.name(field[0]), 'type_index': field[1]}
                                 for field in metadata.fields[row[8]:row[8]+row[18]]]})
    assert len(types) == len(names)
    sources = []
    for relative in ['mini_masukja/gather_geometry.py', 'mini_masukja/source_life_observation.py',
                     'resources/ui-rules/execution-default.json', 'resources/ui-rules/observation-geometry.json']:
        source = ROOT.parent / 'masukja-mini-1.1.27' / relative
        raw = source.read_bytes()
        destination = OUT / 'mini27' / relative
        destination.parent.mkdir(parents=True, exist_ok=True)
        destination.write_bytes(raw)
        sources.append({'source': str(source), 'snapshot': destination.relative_to(ROOT).as_posix(),
                        'sha256': hashlib.sha256(raw).hexdigest(), 'bytes': len(raw)})
    existing = ['data/arbeit-life-source/mini27/source_life_port.py',
                'data/arbeit-life-source/mini27/automation.py', 'data/arbeit-movement-map.json']
    for relative in existing:
        sources.append({'snapshot': relative, 'sha256': hashlib.sha256((ROOT/relative).read_bytes()).hexdigest()})
    payload = {'schema': 'masukja.life-observer-source.v1',
               'created_at': datetime.now(timezone.utc).isoformat(),
               'metadata_sha256': hashlib.sha256(metadata.data).hexdigest(),
               'metadata_snapshot': module.META.relative_to(ROOT).as_posix(),
               'metadata_origin': r'C:\Nexon\MabinogiMobile\MabinogiMobile_Data\il2cpp_data\Metadata\global-metadata.dat',
               'client_version': 'content pinned by metadata SHA256; game version number not inferred',
               'types': types, 'sources': sources, 'process_reads': 0, 'game_inputs': 0,
               'status': 'offline source declarations, not live verification',
               'known_gaps': ['route ID to Gatherable/Fishery raw ID static join not recovered',
                              'typed link enabled/caption field mapping not recovered; own selection receipt + same owner uses mini27 link contract',
                              'free stop actuator not implemented here; activity observations never issue stop',
                              'new aspect ratios require a complete source-bound selection tape']}
    (OUT/'declarations.json').write_text(json.dumps(payload, ensure_ascii=False, indent=2)+'\n', encoding='utf-8')
    print(json.dumps({'declarations': len(types), 'source_snapshots': len(sources), 'process_reads': 0, 'game_inputs': 0}))


if __name__ == '__main__':
    main()
