"""Export a declared string-valued object map without dropping its keys.
python object-map-to-csv.py input.json new-output.csv
"""
import csv,json,sys
from pathlib import Path
def unique(pairs):
    out={}
    for key,value in pairs:
        if key in out: raise ValueError('duplicate object key: '+repr(key))
        out[key]=value
    return out
def reject_constant(value): raise ValueError('nonstandard JSON constant: '+value)
def main():
    raw=Path(sys.argv[1]).read_bytes()
    if len(raw)>2000000:raise ValueError('recipe input cap: 2000000 bytes')
    source=json.loads(raw.decode('utf-8'),object_pairs_hook=unique,parse_constant=reject_constant)
    if not isinstance(source,dict) or not source:raise ValueError('expected nonempty object map')
    rows=[]
    for key,record in source.items():
        if not isinstance(record,dict):raise ValueError('record must be an object: '+repr(key))
        if 'record_id' in record:raise ValueError('reserved record_id collision: '+repr(key))
        if set(record)!={'city','plan'} or not all(isinstance(v,str) for v in record.values()):
            raise ValueError('declared city and plan string fields required: '+repr(key))
        rows.append({'record_id':key,'city':record['city'],'plan':record['plan']})
    with Path(sys.argv[2]).open('x',encoding='utf-8',newline='') as f:
        w=csv.DictWriter(f,fieldnames=['record_id','city','plan']);w.writeheader();w.writerows(rows)
if __name__=='__main__':main()
