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

# Release-safe public preflight. It deliberately has no dependency on
# internal_verification/ so the learner lab remains executable after projection.
root=Path(__file__).resolve().parents[2]
EXPECTED_DIMS=['exact_baseline','ann_tradeoff','metric_contract','hybrid_fusion','filter_boundary','embedding_provenance']
EXPECTED_LABELS={
 'Q1':{'query':'connection pool timeout','category':'database','vector':[0.98,0.10,0.02,0.00],'expected':[1,2],'hard_negative':[6]},
 'Q2':{'query':'password reset access','category':'account','vector':[0.05,0.98,0.08,0.00],'expected':[3],'hard_negative':[]},
 'Q3':{'query':'refund invoice charge','category':'billing','vector':[0.02,0.05,0.98,0.10],'expected':[4,5],'hard_negative':[]}}

def load(p): return json.loads(Path(p).read_text())
def strip_sql(s):
    s=re.sub(r'/\*.*?\*/',' ',s,flags=re.S)
    return re.sub(r'--[^\n]*',' ',s)
def labels_exact(obj):
    if obj.get('embedding_version')!='support-embed-v1' or obj.get('dimension')!=4: return False
    qs={q.get('id'):q for q in obj.get('queries',[])}
    if set(qs)!=set(EXPECTED_LABELS): return False
    for k,e in EXPECTED_LABELS.items():
        q=qs[k]
        if q.get('query')!=e['query'] or q.get('category')!=e['category'] or q.get('query_vector')!=e['vector'] or q.get('expected_relevant_doc_ids')!=e['expected'] or q.get('hard_negative_doc_ids',[])!=e['hard_negative']: return False
    return True
def schema_semantics(text):
    ex=strip_sql(text)
    req=['CREATE TABLE support_docs','id integer PRIMARY KEY','category text NOT NULL','slug text NOT NULL UNIQUE','embedding_version text NOT NULL','embedding vector(4) NOT NULL','textsearch tsvector GENERATED ALWAYS','CREATE INDEX support_docs_textsearch_gin']
    return all(x in ex for x in req) and 'DROP TABLE' not in ex and 'CREATE EXTENSION' not in ex
def seed_rows(text):
    rows=[]
    for line in text.splitlines():
        line=line.strip().rstrip(',;')
        if line.startswith('(') and "'support-embed-" in line:
            try:
                m=re.match(r"\((\d+),'([^']+)','([^']+)','([^']*)','([^']*)','([^']+)'\s*,'\[([^]]+)\]'\)",line)
                if m: rows.append((int(m.group(1)),m.group(2),m.group(3),m.group(6),[float(x) for x in m.group(7).split(',')]))
            except Exception: pass
    return rows
def seed_semantics(text):
    rows=seed_rows(text)
    return len(rows)==8 and [r[0] for r in rows]==list(range(1,9)) and all(r[3]=='support-embed-v1' and len(r[4])==4 for r in rows) and rows[7][2]=='json-schema'
def workbench_semantics(text):
    ex=strip_sql(text)
    required=['__TODO_DISTANCE_OPERATOR__','__TODO_ANN_INDEX_DDL__','__TODO_ANN_PARAMETER_SQL__','__TODO_FUSION_SQL__','__TODO_FILTER_COMPARE_SQL__','__TODO_ACCEPTANCE_DECISION__','enable_indexscan = off','enable_seqscan = off','websearch_to_tsquery','DAT009_EVIDENCE_EXACT','DAT009_EVIDENCE_ANN','DAT009_EVIDENCE_LEXICAL','DAT009_EVIDENCE_FUSION','DAT009_EVIDENCE_FILTER']
    return all(x in text for x in required) and all(x in ex for x in ['CREATE TEMP TABLE dat009_queries','ORDER BY d.embedding','ts_rank_cd']) and 'lexical_score + distance' not in ex.lower()
def probe_semantics(text):
    return all(x in text for x in ['__TODO_PROBE_EXACT_VS_ANN_RECALL_SQL__','__TODO_PROBE_FILTER_CANDIDATE_LOSS_SQL__','__TODO_PROBE_EMBEDDING_VERSION_BREAK_SQL__','__TODO_PROBE_NAIVE_SCORE_ADDITION_SQL__','DAT009_PROBE_EXACT_VS_ANN_RECALL','DAT009_PROBE_FILTER_CANDIDATE_LOSS','DAT009_PROBE_EMBEDDING_VERSION_BREAK','DAT009_PROBE_NAIVE_SCORE_ADDITION','ROLLBACK'])

checks=[]
def ck(n,c): checks.append((n,bool(c)))
mc=load(root/'learner/model-contract.json'); status=load(root/'learner/evidence/runtime-status.json'); labels=load(root/'learner/fixture/query-labels.json'); policy=load(root/'learner/toolchain-policy.json')
work=(root/'learner/sql/03_hybrid_workbench.template.sql').read_text(); seed=(root/'learner/sql/02_seed.sql').read_text(); schema=(root/'learner/sql/01_schema.sql').read_text(); probes=(root/'learner/sql/04_failure_probes.template.sql').read_text()
ck('dimensions_exact',mc.get('required_dimensions')==EXPECTED_DIMS)
ck('five_decisions_open',len(mc.get('starter_decisions_left_open',[]))==5)
ck('labels_exact',labels_exact(labels))
ck('schema_semantics',schema_semantics(schema))
ck('seed_semantics',seed_semantics(seed))
ck('workbench_semantics',workbench_semantics(work))
ck('probe_semantics',probe_semantics(probes))
ck('runtime_unclaimed',status.get('status')=='NOT_RUN_BY_AUTHORING_BUILD' and status.get('runtime_claimable') is False)
ck('local_guard',policy.get('network_policy')=='LOCAL_ONLY' and policy.get('database_name_prefix')=='maknow_dat009_')
ck('version_guard',policy.get('postgresql_major')=='18' and policy.get('pgvector_version')=='0.8.6')
ck('runner_exists',(root/'learner/src/run_local_lab.py').exists())
ck('submission_readme',(root/'learner/submission/README.md').exists())
for n,ok in checks: print(('PASS' if ok else 'FAIL'),n)
print(f'{sum(x for _,x in checks)}/{len(checks)} PASS')
sys.exit(0 if all(x for _,x in checks) and len(checks)==12 else 1)
