#!/usr/bin/env python3
"""Reproduce a synthetic retrieval/citation component check with Python stdlib.

No model, embeddings, hosted service, authentication provider, or network is used.
Query metadata is supplied by the protocol, not inferred from natural language.
"""
import argparse
import csv
import hashlib
import json
import platform
import re
from datetime import date, datetime, timezone
from pathlib import Path

ROOT = Path(__file__).resolve().parent


def tokens(text):
    return set(re.findall(r'[a-z0-9]+', text.lower()))


def eligible(document, query):
    day = date.fromisoformat(query['as_of'])
    start = date.fromisoformat(document['valid_from'])
    end = date.fromisoformat(document['valid_until']) if document['valid_until'] else None
    return (document['product'] == query['product']
            and document['claim_key'] == query['claim_key']
            and document['record_type'] in ('any', query['record_type'])
            and document['audience'] in ('public', query['role'])
            and start <= day and (end is None or day < end))


def retrieve(documents, query, filtered):
    candidates = [d for d in documents if not filtered or eligible(d, query)]
    scored = [(len(tokens(query['question']) & tokens(d['passage'])), d) for d in candidates]
    scored = [(score, d) for score, d in scored if score > 0]
    # Stable input order breaks exact lexical ties in the unfiltered control.
    # A staff-specific policy takes precedence over a public default for staff.
    scored.sort(key=lambda row: (row[0], int(filtered and row[1]['audience'] == query['role']
                                            and query['role'] != 'public')), reverse=True)
    return scored[0][1] if scored else None


def check_citation(documents, query, citation):
    document = next((d for d in documents if d['doc_id'] == citation['doc_id']), None)
    if document is None:
        return False, 'unknown_source'
    if not eligible(document, query):
        return False, 'source_not_eligible'
    if not citation['quote'] or citation['quote'] not in document['passage']:
        return False, 'quote_not_in_source'
    if citation['claim_key'] != document['claim_key'] or citation['claim_value'] != document['claim_value']:
        return False, 'claim_value_mismatch'
    return True, 'accepted'


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--output', default='results.json')
    args = parser.parse_args()
    protocol_path = ROOT / 'protocol.json'
    protocol = json.loads(protocol_path.read_text(encoding='utf-8'))
    documents = protocol['documents']
    retrieval = []
    for case in protocol['retrieval_cases']:
        for mode in ('unfiltered_control', 'metadata_filtered'):
            actual = retrieve(documents, case['query'], mode == 'metadata_filtered')
            actual_id = actual['doc_id'] if actual else None
            actual_value = actual['claim_value'] if actual else None
            retrieval.append({'case_id': case['case_id'], 'mode': mode, 'question': case['query']['question'],
                              'as_of': case['query']['as_of'], 'role': case['query']['role'],
                              'expected_doc_id': case['expected_doc_id'], 'actual_doc_id': actual_id,
                              'expected_value': case['expected_value'], 'actual_value': actual_value,
                              'passed': actual_id == case['expected_doc_id'] and actual_value == case['expected_value']})
    citations = []
    for case in protocol['citation_cases']:
        accepted, reason = check_citation(documents, case['query'], case['citation'])
        citations.append({'case_id': case['case_id'], 'expected_accepted': case['expected_accepted'],
                          'actual_accepted': accepted, 'expected_reason': case['expected_reason'],
                          'actual_reason': reason,
                          'passed': accepted == case['expected_accepted'] and reason == case['expected_reason']})
    summary = {mode: {'passed': sum(r['passed'] for r in retrieval if r['mode'] == mode),
                      'total': sum(r['mode'] == mode for r in retrieval)}
               for mode in ('unfiltered_control', 'metadata_filtered')}
    summary['citation_checks'] = {'passed': sum(r['passed'] for r in citations), 'total': len(citations)}
    result = {'protocol_version': protocol['protocol_version'],
              'executed_at_utc': datetime.now(timezone.utc).isoformat(),
              'environment': {'python': platform.python_version(), 'os': platform.system(), 'architecture': platform.machine()},
              'scope': protocol['scope'], 'protocol_sha256': hashlib.sha256(protocol_path.read_bytes()).hexdigest(),
              'runner_sha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
              'summary': summary, 'retrieval': retrieval, 'citations': citations}
    output = Path(args.output)
    output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
    for name, rows in [('retrieval-results.csv', retrieval), ('citation-results.csv', citations)]:
        with output.with_name(name).open('w', newline='', encoding='utf-8') as handle:
            writer = csv.DictWriter(handle, fieldnames=list(rows[0]))
            writer.writeheader()
            writer.writerows(rows)
    print(json.dumps({'summary': summary, 'protocol_sha256': result['protocol_sha256']}, ensure_ascii=False))
    if any(not row['passed'] for row in retrieval if row['mode'] == 'metadata_filtered') or any(not r['passed'] for r in citations):
        raise SystemExit(1)


if __name__ == '__main__':
    main()
