"""Read-only WSP research checks; no agent, LLM, GPU or production protocol.

Run with an interpreter providing tokenizers==0.23.3; see README.md.
Only the explicitly requested JSON output is written.
"""
import argparse
import ast
import ctypes
import hashlib
import io
import json
from pathlib import Path
import subprocess
import struct
import sys
import unittest


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--wsp-repo', type=Path, required=True)
    parser.add_argument('--tokenizer', type=Path, required=True)
    parser.add_argument('--output', type=Path, required=True)
    args = parser.parse_args()
    root = Path(__file__).resolve().parents[3]
    wsp = args.wsp_repo.resolve()
    sys.path.insert(0, str(wsp))
    from rogex_wsp.primitives import Primitive
    from rogex_wsp.symbol import Message, Symbol
    from rogex_wsp.emotion import Emotion
    from rogex_wsp.multiphonic import symbol_to_chords, chords_to_fields
    from rogex_wsp.roundtrip_audio import _BIT_SECONDS, _BIT_GAP_SECONDS
    from tokenizers import Tokenizer

    source = root / 'core/wsp.py'
    atoms = next(ast.literal_eval(node.value) for node in ast.parse(source.read_text()).body
                 if isinstance(node, ast.Assign)
                 and any(isinstance(t, ast.Name) and t.id == 'ATOMS' for t in node.targets))
    proposed = ('SER NO_SER YO OTRO TODO NADA UNO MUCHOS AQUI ALLI DENTRO FUERA '
                'CERCA LEJOS AHORA ANTES DESPUES SIEMPRE NUNCA UNIR SEPARAR CAMBIAR '
                'MANTENER OBSERVAR CREAR DESTRUIR MOVER CAUSA EFECTO POSIBLE IMPOSIBLE DESCONOCIDO').split()
    tokenizer = Tokenizer.from_file(str(args.tokenizer))
    line = 'YO -> DYN:PUSH(2) -> DYN:LOCK @DESPUES @ALLI[20,08] | E[V+0.85,C+0.20,U+0.30]'
    value = {'src': 'YO', 'rel': 'DYN:PUSH', 'param': 2, 'dst': 'DYN:LOCK',
             'time': 'DESPUES', 'space': 'ALLI', 'xy': [20, 8], 'E': {'V': .85, 'C': .20, 'U': .30}}
    texts = {
        'proposal_wsp_line': line,
        'equivalent_named_json': json.dumps(value, separators=(',', ':')),
        'equivalent_positional_json': json.dumps(list(value.values()), separators=(',', ':')),
    }
    token_counts = {name: {'text': text, 'utf8_bytes': len(text.encode()),
                           'tokens': len(tokenizer.encode(text, add_special_tokens=False).ids)}
                    for name, text in texts.items()}
    symbols = [Symbol(source=Primitive.YO, relation=Primitive.UNIR, target=Primitive.OTRO,
                      time=Primitive.AHORA, emotion=Emotion(certainty=.95)),
               Symbol(source=Primitive.YO)]
    audio_fields = chords_to_fields(symbol_to_chords(symbols[0]))
    # Select only the certainty field, without reconstructing other symbol fields.
    from rogex_wsp.grammar import FieldTag
    certainty = next(f.value for f in audio_fields if f.tag is FieldTag.EMO_CERTAINTY)
    c_source = '#include <stdalign.h>\n#include <stdint.h>\ntypedef struct alignas(32) WSP58_Packet { uint8_t raw[32]; } WSP58_Packet;\n'
    compilation = subprocess.run(['cc', '-std=c11', '-pedantic-errors', '-x', 'c',
                                  '-fsyntax-only', '-'], input=c_source, text=True,
                                 capture_output=True, timeout=10)
    c_prefix = bytes([0x57, 0x54, 2, 0x41, 0x47, 2, 14, 9])
    c_key = int.from_bytes(c_prefix, 'little') & 0xFFFFFFFFFFFF0000
    python_key = int.from_bytes(c_prefix[2:8], 'big')
    stream = io.StringIO()
    loader = unittest.TestLoader()
    suite = unittest.TestSuite(loader.discover(str(wsp / 'tests'), pattern=pattern)
                               for pattern in ('test_core.py', 'test_roundtrip_audio_041.py',
                                               'test_multiphonic.py'))
    test_result = unittest.TextTestRunner(stream=stream, verbosity=1).run(suite)
    results = {
        'date': '2026-10-17', 'scope': 'offline inspection, lexical counts and selected upstream tests; not ARC evaluation',
        'wsp_commit': subprocess.check_output(['git', '-C', str(wsp), 'rev-parse', 'HEAD'], text=True).strip(),
        'echo_core_wsp_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
        'tokenizer_sha256': hashlib.sha256(args.tokenizer.read_bytes()).hexdigest(),
        'tokenizer_vocab_including_added': tokenizer.get_vocab_size(with_added_tokens=True),
        'token_counts': token_counts,
        'sizes_bytes': {'misquoted_format': struct.calcsize('>BBBB6bBBBB'),
                        'echo_actual_format': struct.calcsize('<BBBBbbbbbbBBBBBB'),
                        'upstream_message_examples': [len(Message(packets=[s]).to_bytes()) for s in symbols],
                        'arbitrary_64x64_16color_frame': 64 * 64 * 4 // 8},
        'legacy_primitive_ids_equal': all(atoms[p.code] == p.name for p in Primitive),
        'proposal_changed_primitive_slots': sum(a != b for a, b in zip(atoms, proposed)),
        'polyphonic_certainty': {'before': .95, 'after': certainty},
        'fsk_nominal_bits_per_second': 1 / (_BIT_SECONDS + _BIT_GAP_SECONDS),
        'fsk_32byte_payload_seconds_excluding_framing': 32 * 8 * (_BIT_SECONDS + _BIT_GAP_SECONDS),
        'c11_proposed_declaration': {'source': c_source, 'returncode': compilation.returncode,
                                      'diagnostic': compilation.stderr[:1000]},
        'proposed_cam_keys': {'c_little_endian': c_key, 'python': python_key, 'equal': c_key == python_key},
        'ctypes_uint8_999': ctypes.c_uint8(999).value,
        'selected_upstream_tests': {'run': test_result.testsRun, 'failures': len(test_result.failures),
                                    'errors': len(test_result.errors), 'skipped': len(test_result.skipped),
                                    'log': stream.getvalue()},
        'limitations': ['No chat template, dictionary setup, framing, receipts or raw image payload in token counts.',
                        'Only named JSON and positional JSON carry the same fields as the supplied WSP example.',
                        'No GPU inference, physical acoustic channel, IPC latency or ARC score measured.',
                        'C compilation is a minimal extraction of the proposed declaration, not a full implementation test.'],
    }
    args.output.write_text(json.dumps(results, indent=2, ensure_ascii=False) + '\n')
    print(json.dumps({k: results[k] for k in ('token_counts', 'sizes_bytes', 'proposal_changed_primitive_slots',
                                             'polyphonic_certainty', 'selected_upstream_tests')}, indent=2))


if __name__ == '__main__':
    main()
