#!/usr/bin/env python3
from pathlib import Path
import argparse,json,re

p=argparse.ArgumentParser(description="WEB019-LAB1 bounded structural/evidence checker; it does not certify browser or assistive-technology rendering.")
p.add_argument('--structure-only',action='store_true',help='Validate the intentionally incomplete starter/transfer fixture only.')
a=p.parse_args()
HERE=Path(__file__).resolve().parent
ROOT=HERE if (HERE/'learner').is_dir() else HERE.parent
checks=[]
def add(name,ok,detail=''): checks.append((name,bool(ok),detail))
def text(path): return path.read_text(encoding='utf-8')
def has_order(html,ids):
    pos=[html.find(f'id="{x}"') for x in ids]
    return all(x>=0 for x in pos) and pos==sorted(pos)

common_required=['learner/starter/index.html','learner/starter/styles.css','learner/starter/app.js','learner/transfer/starter.html','learner/transfer/styles.css','learner/transfer/app.js','learner/evidence/learner_evidence.json']
for rel in common_required: add('required_file:'+rel,(ROOT/rel).is_file())
if not all((ROOT/r).is_file() for r in common_required):
    for n,ok,d in checks: print(('PASS' if ok else 'FAIL')+' | '+n+((' | '+d) if d else ''))
    raise SystemExit(1)

starter_html=text(ROOT/'learner/starter/index.html')
starter_css=text(ROOT/'learner/starter/styles.css')
starter_js=text(ROOT/'learner/starter/app.js')
transfer_html=text(ROOT/'learner/transfer/starter.html')
transfer_js=text(ROOT/'learner/transfer/app.js')
blob='\n'.join(text(ROOT/r) for r in common_required if (ROOT/r).suffix in {'.html','.css','.js','.md'})
add('portable:no_external_http_dependency',not re.search(r'https?://',blob,re.I))
add('fixture:persian_lang_present','<html lang="fa">' in starter_html)
add('fixture:dynamic_risk_classes_present',all(x in starter_js for x in ['displayName','orderCode','orderId','amount','"2048"','"1,250,000"']))
expected_ids=['actor-label','display-name','message-label','order-code','order-id-label','order-id','amount-label','amount']
add('fixture:business_source_order',has_order(starter_html,expected_ids))
add('fixture:business_order_contract','actor -> displayName -> message -> orderCode -> orderId -> amount -> completion' in starter_js)
add('fixture:transfer_order_contract','support-label -> displayName -> ticket-label -> ticketCode -> completion' in transfer_js)
add('transfer:not_presolved',not re.search(r'\bdir\s*=\s*["\'](?:rtl|ltr|auto)["\']',transfer_html,re.I) and '<bdi' not in transfer_html.lower())

try:
    ev=json.loads(text(ROOT/'learner/evidence/learner_evidence.json'))
    add('evidence:valid_json',True)
except Exception as e:
    ev={'rows':[]}; add('evidence:valid_json',False,str(e))

if a.structure_only:
    add('starter:intentional_missing_structural_rtl',not re.search(r'\bdir\s*=\s*["\']rtl["\']',starter_html,re.I))
    add('starter:intentional_alignment_shortcut_present','text-align:right' in starter_css.replace(' ',''))
    add('starter:not_presolved_with_bdi','<bdi' not in starter_html.lower())
    add('evidence:starter_rows_allowed_empty',isinstance(ev.get('rows'),list) and len(ev.get('rows'))==0,'rows='+str(len(ev.get('rows',[]))))
else:
    sub=ROOT/'learner/submission'
    for name in ['index.html','styles.css','app.js']:
        add('submission:required_file:'+name,(sub/name).is_file())
    if all((sub/name).is_file() for name in ['index.html','styles.css','app.js']):
        h=text(sub/'index.html'); j=text(sub/'app.js')
        add('submission:structural_rtl_present',bool(re.search(r'\bdir\s*=\s*["\']rtl["\']',h,re.I)))
        add('submission:source_order_preserved',has_order(h,expected_ids))
        add('submission:dynamic_isolation_present','<bdi' in h.lower())
        add('submission:no_reverse_source_mutation_marker','reverse-source' not in j and 'WEB019_MUTATION' not in j)
    rows=ev.get('rows') if isinstance(ev,dict) else None
    add('evidence:rows_minimum_3',isinstance(rows,list) and len(rows)>=3,'rows='+str(len(rows or [])))
    fields=['scenario_id','input_string','source_order','base_direction','isolation_mechanism','layout_mechanism','dom_observation','visual_observation','observation_vs_inference','environment','residual_limitation']
    if isinstance(rows,list):
        for i,row in enumerate(rows):
            add(f'evidence:row_{i}_core_complete',all(str(row.get(f,'')).strip() for f in fields))
        add('evidence:distinct_baseline',any(r.get('changed_context') is False and not str(r.get('mutation','')).strip() for r in rows))
        add('evidence:distinct_mutation',any(r.get('changed_context') is False and str(r.get('mutation','')).strip() and all(str(r.get(f,'')).strip() for f in ['prediction','observed_failure','diagnosis','repair','post_repair_observation']) for r in rows))
        add('evidence:distinct_transfer',any(r.get('changed_context') is True and not str(r.get('mutation','')).strip() for r in rows))

print(f"WEB019-LAB1 public checks: {sum(ok for _,ok,_ in checks)}/{len(checks)} PASS")
print('MODE | '+('STRUCTURE_ONLY' if a.structure_only else 'COMPLETED_SUBMISSION'))
print('BOUNDARY | Static structure/evidence only; no browser/AT correctness is certified.')
for n,ok,d in checks: print(('PASS' if ok else 'FAIL')+' | '+n+((' | '+d) if d else ''))
raise SystemExit(0 if all(ok for _,ok,_ in checks) else 1)
