"""Build the occurrence map from the Table S1 export; preserve frozen path geometry."""
from pathlib import Path
import json,re,shutil,hashlib,argparse

def copy_file(source, destination):
    source=Path(source); destination=Path(destination)
    if source.resolve()!=destination.resolve():
        shutil.copy2(source,destination)

def build(data_dir, destination):
    data_dir=Path(data_dir); destination=Path(destination); destination.mkdir(parents=True,exist_ok=True)
    cases=json.loads((data_dir/'local_cases.json').read_text())
    if isinstance(cases,dict): cases=cases['cases']
    html=(data_dir/'map_template.html').read_text()
    old=json.loads((data_dir/'map_points.json').read_text()); routes=json.loads((data_dir/'map_routes.json').read_text())
    comparators=[p for p in old if p.get('group')!='core_sites']
    by_old={p['key']:p for p in old}; mapped=[c for c in cases if c['latitude'] is not None and c['longitude'] is not None]
    assert len(cases)==24 and len({c['site_id'] for c in cases})==24
    assert len(mapped)==9 and all(c['map_eligible'] and c['coordinate_status'].lower().startswith('verified') for c in mapped)
    n=len(cases); m=len(mapped); points=list(comparators)
    modelled={r['target_key'] for r in routes}
    for c in mapped:
        p=dict(by_old.get(c['site_id'],{}))
        citation_parts=c['citation'].split(' | ')
        publication_urls=[c['source_url'],*c.get('additional_source_urls',[])]
        publication_refs=[
            {'label':label,'url':publication_urls[index] if index < len(publication_urls) else ''}
            for index,label in enumerate(citation_parts)
        ]
        p.update(key=c['site_id'],name=c['canonical_name'],label_name=c['canonical_name'],coords=[c['latitude'],c['longitude']],type='site',group='core_sites',
            evidence_filter_key=c['category_key'],evidence_strength_label=c['evidence_category'],evidence_strength_summary=c['limitation'],
            source_attribution_status=c['source_attribution'],date_label=c['site_chronology'],date_basis='',date_reference=c['short_citation'],
            site_summary=c['site_description'],obsidian_context=c['archaeological_evidence_basis'],
            spatial_treatment=c['spatial_treatment'],note=c['coordinate_note'],
            refs=[*publication_refs,{'label':c['coordinate_source'],'url':c['coordinate_url']}],
            obsidian_chronology=c['obsidian_chronology'],location_status=c['coordinate_status'],coordinate_source=c['coordinate_source'],
            limitation=c['limitation'],context=c['context'],analytical_technique=c['analytical_technique'] or 'None reported',
            obsidian_quantity_text=c['obsidian_quantity_text'],artefact_forms=c['artefact_forms'])
        if c['site_id'] in modelled:
            frozen=next(r['path'][-1] for r in routes if r['target_key']==c['site_id'])
            p['note']+=' Model paths retain their original endpoint '+', '.join(f'{x:.6f}' for x in frozen)+'. The verified location remains in the original model grid cell; the review check found no change to the calculated cell path or cost. Updating only the endpoint connector changed total analytical length by less than 0.06%. The original path remains displayed.'
        points.append(p)
    # Register records are authoritative; map_points contains only sources/comparators.
    sourceIndex=json.loads((data_dir/'map_sourceIndex.json').read_text())
    seen={item['label'] for item in sourceIndex[0]['items']}
    for c in cases:
        labels=c['citation'].split(' | ')
        urls=[c['source_url'],*c.get('additional_source_urls',[])]
        refs=[(label,urls[i] if i<len(urls) else '') for i,label in enumerate(labels)]
        for label,url in [*refs,(c['coordinate_source'],c['coordinate_url'])]:
            if label and label not in seen:
                seen.add(label)
                sourceIndex[0]['items'].append({'label':label,'url':url,'note':''})
    for name,value in [('points',points),('routes',routes),('sourceIndex',sourceIndex),('paperEvidenceRegister',cases)]:
        token='{{'+name+'}}'
        assert html.count(token)==1, token
        html=html.replace(token,json.dumps(value,ensure_ascii=False,separators=(',',':')))
    html=html.replace('{{EVIDENCE_TABLE_JS}}',(data_dir/'evidence_table.js').read_text())
    s2=json.loads((data_dir/'map_table_s2.json').read_text())
    from html import escape
    rows=[]
    for row in s2['rows']:
        route=next(r for r in routes if r['key']==row['route_key'])
        rows.append('<tr><th scope="row">'+escape(route['name']+' — '+row['analysis_role']+(' (illustrative endpoint)' if row['endpoint_status']=='illustrative' else ''))+'</th>'+''.join(
            '<td>'+f"{row[key]:.1f}"+'</td>' for key in ['grid_path_length_km','endpoint_connectors_km','total_path_length_km','straight_line_km','cost_relative_to_harb_central_100'])+'</tr>')
    table='<div class="principal-table-wrap" tabindex="0" role="region" aria-label="Complete 22-path matrix"><table class="principal-table"><caption>Table S2. Complete matrix at the original endpoints</caption><thead><tr><th scope="col">Scenario</th><th scope="col">Grid path (km)</th><th scope="col">Both connectors (km)</th><th scope="col">Total path (km)</th><th scope="col">Straight line (km)</th><th scope="col">Relative cost</th></tr></thead><tbody>'+''.join(rows)+'</tbody></table></div>'
    html=html.replace('{{PRINCIPAL_TABLE}}',table)
    assert '{{' not in html, 'Unfilled template placeholder'
    (destination/'index.html').write_text(html)
    (destination/'data/evidence').mkdir(parents=True,exist_ok=True); (destination/'source').mkdir(exist_ok=True)
    copy_file(data_dir/'local_cases.json',destination/'data/evidence/local_cases.json')
    copy_file(__file__,destination/'source/build_map.py')
    for fn in ['map_template.html','map_points.json','map_routes.json','map_sourceIndex.json','map_table_s2.json','evidence_table.js','local_cases.json']:
        copy_file(data_dir/fn,destination/'source'/fn)
    return {'retained':n,'mapped':m,'unmapped':n-m,'local_model_targets':6,'comparators':4,'model_scenarios':len(routes)}

if __name__=='__main__':
    p=argparse.ArgumentParser();p.add_argument('--data-dir',default=Path(__file__).parent);p.add_argument('--output',required=True);a=p.parse_args();print(json.dumps(build(a.data_dir,a.output),indent=2))
