#!/usr/bin/env python3
"""Read-only lexical search of the supplied research package. Python standard library only."""
import argparse,json,re,sqlite3
from pathlib import Path

VOCAB=json.loads(Path(__file__).with_name('search-vocabulary.json').read_text())
def prepare_query(query,company=None):
    tokens=re.findall(r'\w+',query.lower(),flags=re.UNICODE)
    inferred=next((c for c in ['Cove','Visitt','PRISM'] if c.lower() in tokens),None)
    if not inferred and re.search(r'\bhow (?:does|can) visit\b',query,re.I):inferred='Visitt'
    company=company or inferred
    ignored=set(VOCAB['stopwords']+['cove','visitt','prism'])
    if inferred=='Visitt':ignored.add('visit')
    terms=list(dict.fromkeys(t for t in tokens if t not in ignored))
    expanded=list(terms)
    for group in VOCAB['synonym_groups']:
        if any(t in group for t in terms):expanded.extend(group)
    return list(dict.fromkeys(expanded)),company

def resolve_sources(db,record):
    record['resolved_sources']=[]
    for sid in record['source_ids']:
        s=json.loads(db.execute('SELECT payload_json FROM sources WHERE id=?',(sid,)).fetchone()[0])
        compact={k:s[k] for k in ['id','title','publisher','url']}
        compact['source_dates']=list(dict.fromkeys(o['date'] for o in s['observations'] if o.get('date')))
        compact['notes']=list(dict.fromkeys(o.get('note',o.get('evidence_note')) for o in s['observations'] if o.get('note') or o.get('evidence_note')))
        record['resolved_sources'].append(compact)
    return record

def search(query,kind=None,company=None,limit=5,mode='auto'):
    dbpath=Path(__file__).resolve().with_name('knowledge.sqlite')
    db=sqlite3.connect(dbpath.as_uri()+'?mode=ro',uri=True)
    terms,company=prepare_query(query,company)
    if not terms:db.close();return []
    match=' OR '.join('"'+t.replace('"','""')+'"' for t in terms)
    sql='''SELECT r.payload_json FROM search_index s JOIN records r ON r.id=s.id WHERE search_index MATCH ?'''
    args=[match]
    if kind:sql+=' AND r.kind=?';args.append(kind)
    if company:sql+=' AND instr(lower(r.companies),lower(?))>0';args.append(company)
    workflow=mode=='workflow' or (mode=='auto' and bool(re.search(r'\b(how|where)\b',query,re.I)))
    if workflow and not kind:
        sql+=" AND r.kind IN ('workflow_answer','documentation','flow','claim')"
        sql+=" ORDER BY CASE r.kind WHEN 'workflow_answer' THEN 0 WHEN 'documentation' THEN 1 WHEN 'flow' THEN 2 ELSE 3 END, bm25(search_index,0,4,1,2) LIMIT ?"
    else:
        sql+=" ORDER BY CASE r.kind WHEN 'claim' THEN 0 WHEN 'gap' THEN 1 WHEN 'workflow_answer' THEN 2 WHEN 'flow' THEN 3 WHEN 'documentation' THEN 4 WHEN 'review' THEN 5 WHEN 'analysis' THEN 6 WHEN 'task' THEN 7 WHEN 'asset' THEN 8 ELSE 9 END, bm25(search_index,0,4,1,2) LIMIT ?"
    args.append(limit)
    results=[]
    for (raw,) in db.execute(sql,args):
        results.append(resolve_sources(db,json.loads(raw)))
    db.close();return results

def get_record(record_id):
    db=sqlite3.connect(Path(__file__).resolve().with_name('knowledge.sqlite').as_uri()+'?mode=ro',uri=True)
    row=db.execute('SELECT payload_json FROM records WHERE id=?',(record_id,)).fetchone()
    result=resolve_sources(db,json.loads(row[0])) if row else None
    db.close();return result

def main():
    p=argparse.ArgumentParser(description=__doc__)
    p.add_argument('query',nargs='?',default='')
    p.add_argument('--type',dest='kind',choices=['claim','gap','workflow_answer','documentation','flow','review','analysis','task','asset','source'])
    p.add_argument('--mode',choices=['auto','workflow','research'],default='auto',help='Auto prioritizes workflow references for how/where questions. This is lexical lookup, not generated Q&A.')
    p.add_argument('--company',choices=['Cove','Visitt','PRISM'])
    p.add_argument('--limit',type=int,default=5)
    p.add_argument('--id',help='Fetch one exact record ID; ignores search filters')
    p.add_argument('--json',action='store_true')
    a=p.parse_args()
    if not 1<=a.limit<=100:p.error('--limit must be between 1 and 100')
    if not a.query and not a.id:p.error('Supply a query or --id')
    if a.id:
        row=get_record(a.id);results=[row] if row else []
    else:results=search(a.query,a.kind,a.company,a.limit,a.mode)
    if a.json:print(json.dumps(results,ensure_ascii=False,indent=2));return
    for r in results:
        print(r['id']+' | '+r['title']+'\n'+r['evidence_status']+' | as of '+r['as_of'])
        print(r['text'])
        if r['artifact_path']:print('Package artifact: '+r['artifact_path'])
        for s in r.get('resolved_sources',[]):print('Source: '+s['title']+' — '+s['url'])
        print()
    if not results:print('No matching records. Try fewer terms, another type, or an exact ID. Absence of a result is not evidence of missing product capability.')

if __name__=='__main__':main()
