#!/usr/bin/env python3
"""Offline educational retrieval demo. No LLM, embeddings, network or real customer data.

Python 3.10+. A fixture principal is not an authentication mechanism. In a real
service, identity and document metadata must come from trusted server-side systems.
"""
import argparse
from datetime import date
import hashlib
import json
from pathlib import Path
import re
import unicodedata

def terms(text):
    text=''.join(c for c in unicodedata.normalize('NFKD',text.lower()) if not unicodedata.combining(c))
    return set(re.findall(r'[^\W_]+(?:-[^\W_]+)*',text)) - {'le','la','les','de','des','du','un','une','et','pour','the','a','an','of','and','for'}

def eligible(document, principal, as_of):
    """Deny missing/invalid metadata; authorisation is applied BEFORE ranking."""
    try:
        tenant=principal['tenant']; roles=principal['roles']
        if not isinstance(tenant,str) or not tenant or not isinstance(roles,list) or not roles:
            return False
        if document['approved'] is not True or document['superseded'] is not False:
            return False
        if not document['source'] or not document['revision']:
            return False
        if document['tenant'] not in (tenant,'public'):
            return False
        if not isinstance(document['roles'],list) or not set(document['roles']) & set(roles):
            return False
        return date.fromisoformat(document['valid_from']) <= as_of < date.fromisoformat(document['valid_until'])
    except (KeyError,TypeError,ValueError):
        return False

def retrieve(documents, query, principal, as_of, limit=3):
    if not 1 <= limit <= 20:
        raise ValueError('limit must be between 1 and 20')
    wanted=terms(query)
    if not wanted:return []
    ranked=[]
    for document in documents:
        if not eligible(document,principal,as_of):continue
        score=len(wanted & terms(document['text']))/len(wanted)
        if score:
            ranked.append((score,document['id'],document))
    ranked.sort(key=lambda row:(-row[0],row[1]))
    return [{'id':d['id'],'score':score,'source':d['source'],'revision':d['revision'],'text':d['text']}
            for score,_,d in ranked[:limit]]

def evaluate(fixture):
    day=date.fromisoformat(fixture['as_of'])
    rows=[]
    for case in fixture['cases']:
        results=retrieve(fixture['documents'],case['query'],case['principal'],day)
        actual=[r['id'] for r in results]
        rows.append({'case':case['id'],'expected':case['expected'],'actual':actual,'passed':actual==case['expected']})
    return {'kind':'synthetic deterministic retrieval checks; not an AI benchmark',
            'as_of':fixture['as_of'],'cases':len(rows),'passed':sum(r['passed'] for r in rows),'results':rows}

def main():
    parser=argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--fixture',type=Path,default=Path(__file__).with_name('fixture.json'))
    args=parser.parse_args()
    raw=args.fixture.read_bytes()
    result=evaluate(json.loads(raw))
    result['fixture_sha256']=hashlib.sha256(raw).hexdigest()
    print(json.dumps(result,ensure_ascii=False,indent=2))
    return 0 if result['cases']==result['passed'] else 1

if __name__=='__main__':raise SystemExit(main())
