#!/usr/bin/env python3
"""Mechanical comparison: the paper's prompt tables (v3 LaTeX, escapes removed) vs the platform's strings.gen.ts.
Whitespace is compared as token boundaries only, because LaTeX source line layout carries no whitespace information.
argv: <v3 dir> <strings.gen.ts>"""
import difflib, json, os, re, sys

v3, gen = sys.argv[1], sys.argv[2]
src = open(gen, encoding='utf-8').read()


def block(name):
    m = re.search(r'export const ' + name + r' = (.*?) as const;', src, re.S)
    if not m:
        raise SystemExit('no block ' + name)
    return json.loads(m.group(1))


def fl(parts):
    return ''.join(p if isinstance(p, str) else '{' + p['v'] + '}' for p in parts)


def lines(rel, a, b):
    with open(os.path.join(v3, rel), encoding='utf-8') as f:
        return ''.join(f.readlines()[a - 1:b])


def delatex(s):
    s = re.sub(r'\\textbf\{\\textit\{(\([^{}]*\))\}\}', r' \1 ', s)
    s = re.sub(r'\\textit\{\\textbf\{(\([^{}]*\))\}\}', r' \1 ', s)
    s = re.sub(r'\\textbf\{(\([^{}]*\))\}', r' \1 ', s)
    s = s.replace('\\qquad', ' ').replace('\\quad', ' ')
    s = s.replace('$<$', '<').replace('$>$', '>')
    s = s.replace('\\{', '\x01').replace('\\}', '\x02')
    s = s.replace('\\_', '_').replace('\\#', '#').replace('\\&', '&')
    # unescaped {x} is a LaTeX group: it prints as x, without braces. Marked «x» so the comparison can show it.
    s = re.sub(r'\{([A-Za-z_]+)\}', '\u00ab\\1\u00bb', s)
    s = s.replace('\x01', '{').replace('\x02', '}')
    return s


def toks(s):
    return s.split()


def cmp(title, paper, plat, src_paper, src_plat):
    a, b = toks(paper), toks(plat)
    print(f'\n=== {title}\n    paper: {src_paper}\n    platform: {src_plat}\n    tokens: paper {len(a)}, platform {len(b)}')
    ops = [o for o in difflib.SequenceMatcher(None, a, b, autojunk=False).get_opcodes() if o[0] != 'equal']
    if not ops:
        print('    IDENTICAL (token sequence)')
    for tag, i1, i2, j1, j2 in ops:
        print(f'    {tag}: paper[{i1}:{i2}] {" ".join(a[i1:i2])!r}  ->  platform[{j1}:{j2}] {" ".join(b[j1:j2])!r}')


CP = block('CHARACTER_PROMPT')
EP = block('ENVIRONMENT_PROMPT')
NP = block('NSP_PROMPT')
NP0 = block('PAPER_ERA_NSP_PROMPT')
J = block('JUDGE')
AGENT = block('AGENT')

# Role-playing instruction, fixed template
paper_tpl = delatex(lines('Tables/prompts_agent.tex', 11, 47))
plat_tpl = (fl(CP['prompt']).replace('{other_character_profiles_str}', fl(CP['othersSection'])).replace('{motivation}', fl(CP['motivationSection']))
            + fl(CP['requirements']).replace('{output_format}', ''))
cmp('Role-playing instruction (fixed template), without the output-format sentence', paper_tpl, plat_tpl, 'Tables/prompts_agent.tex:11-47', 'CHARACTER_PROMPT.prompt + othersSection + motivationSection + requirements')

cmp('Output format, CoSER models', delatex(lines('Tables/prompts_agent.tex', 52, 52)), CP['outputFormat']['thought'], 'Tables/prompts_agent.tex:52', 'CHARACTER_PROMPT.outputFormat.thought')
cmp('Output format, other models', delatex(lines('Tables/prompts_agent.tex', 58, 58)), CP['outputFormat']['thoughtWithExample'], 'Tables/prompts_agent.tex:58', 'CHARACTER_PROMPT.outputFormat.thoughtWithExample')

print('\n--- Text the platform sends to a character that is in no paper table (AGENT.concise):', repr(AGENT['concise']))
print('--- Text the platform sends to name-prefix families that is in no paper table (AGENT.namePrefix):', repr(fl(AGENT['namePrefix'])))
print('--- Separators between other-character profiles (CHARACTER_PROMPT.decorators), not in the paper table:', CP['decorators'])

cmp('Environment model', delatex(lines('Tables/prompts_agent_2.tex', 11, 36)), fl(EP['prompt']).replace('{random.choice(model_roles)}', 'an environment simulator'), 'Tables/prompts_agent_2.tex:11-36', 'ENVIRONMENT_PROMPT.prompt, with the random role fixed to the paper\'s "an environment simulator"')
print('    note: platform draws the role from', EP['modelRoles'], '(paper table prints only "an environment simulator")')

paper_nsp = delatex(lines('Tables/prompts_agent_2.tex', 42, 46))
cmp('Next speaker, the prompt the platform USES', paper_nsp, fl(NP['prompt']).replace('{ENVIRONMENT}', 'Environment'), 'Tables/prompts_agent_2.tex:42-46', 'NSP_PROMPT.prompt (used by setup.ts:133)')
cmp('Next speaker, the recorded paper-era prompt (NOT used)', paper_nsp, fl(NP0['prompt']).replace('{ENVIRONMENT}', 'Environment'), 'Tables/prompts_agent_2.tex:42-46', 'PAPER_ERA_NSP_PROMPT.prompt')

cmp('Judge template', delatex(lines('Tables/prompts_eval.tex', 9, 82)).replace('\\textbf{Template} &', ''), J['template'], 'Tables/prompts_eval.tex:9-82', 'JUDGE.template')

spans = {
    'Anthropomorphism': (13, 13, 19, 48),
    'Character Fidelity': (55, 55, 61, 89),
    'Storyline Quality': (96, 96, 102, 115),
    'Storyline Consistency': (122, 122, 128, 132),
}
for d, (i1, i2, r1, r2) in spans.items():
    cmp(f'{d}: intro', delatex(lines('Tables/prompts_eval_2.tex', i1, i2)), J['details'][d]['dimension_brief'], f'Tables/prompts_eval_2.tex:{i1}', f'JUDGE.details[{d}].dimension_brief')
    cmp(f'{d}: rubrics', delatex(lines('Tables/prompts_eval_2.tex', r1, r2)), J['details'][d]['dimension_criteria'], f'Tables/prompts_eval_2.tex:{r1}-{r2}', f'JUDGE.details[{d}].dimension_criteria')
