import csv,json
from pathlib import Path
from fractions import Fraction
from collections import defaultdict
p=Path(__file__).resolve().parent
def read(n):
 with (p/n).open() as f:return list(csv.DictReader(f))
s=read('industry-step-up-2026.csv');division=[r for r in s if r['row_type']=='industry_division'];unknown=next(r for r in s if r['row_type']=='unknown_reconciliation');national=next(r for r in s if r['row_type']=='national_total')
assert len(division)==19
for r in s:
 assert int(r['end_june_2025'])-int(r['start_june_2024'])==int(r['net_2024_25'])
 assert int(r['end_june_2026'])-int(r['end_june_2025'])==int(r['net_2025_26'])
 assert int(r['net_2025_26'])-int(r['net_2024_25'])==int(r['change_in_annual_net_gain'])
flows={r['period']:r for r in read('national-entries-exits-2022-2026.csv')}
denominator=int(flows['2025-26']['net_change'])-int(flows['2024-25']['net_change'])
assert int(national['change_in_annual_net_gain'])==denominator
assert sum(int(r['change_in_annual_net_gain']) for r in division)+int(unknown['change_in_annual_net_gain'])==denominator
pair=sum(int(r['change_in_annual_net_gain']) for r in division if r['industry'] in ['Construction','Professional, Scientific and Technical Services'])
m=read('employment-movement-2026.csv');non=next(r for r in m if r['category']=='Non-employing')
for r in m:
 assert int(r['entries'])-int(r['exits'])==int(r['entry_exit_balance'])
 assert int(r['entry_exit_balance'])+int(r['net_surviving_movement'])==int(r['net_change'])
assert sum(int(r['net_surviving_movement']) for r in m)==0
assert sum(int(r['net_change']) for r in m)==int(flows['2025-26']['net_change'])
movement_share=100*int(non['net_surviving_movement'])/int(non['net_change'])
raw=read('council-industry-comparison-inputs.csv');bycode=defaultdict(list)
for r in raw:
 if r['industry']!='ALL':bycode[r['code']].append(r)
summary=[];published={r['lga_code']:r for r in read('transport-comparison-2024-2025.csv')}
for minimum in [1000,5000,10000]:
 totals=[r for r in raw if r['industry']=='ALL' and int(r['count_2024'])>=minimum]
 totals.sort(key=lambda r:(-Fraction(int(r['count_2025'])-int(r['count_2024']),int(r['count_2024'])),r['code']))
 flags=[];inclusive=[]
 for rank,r in enumerate(totals,1):
  sectors=bycode[r['code']];best=max(int(t['net_2024_25']) for t in sectors);leaders=[t['industry'] for t in sectors if int(t['net_2024_25'])==best];sole=leaders==['I'];inc='I' in leaders;flags.append(sole);inclusive.append(inc)
  if minimum==5000:
   q=published[r['code']];assert int(q['rank'])==rank;assert (q['transport_sole_leader']=='True')==sole;assert (q['transport_joint_or_sole_leader']=='True')==inc;assert int(q['largest_division_net_change'])==best
 summary.append({'minimum':minimum,'eligible':len(totals),'top10_sole':sum(flags[:10]),'other_sole':sum(flags[10:]),'other_including_ties':sum(inclusive[10:])})
assert [(r['eligible'],r['top10_sole'],r['other_sole']) for r in summary]==[(301,9,25),(135,8,14),(81,7,8)]
print(json.dumps({'step_up_pair':pair,'step_up_denominator':denominator,'unknown_step':int(unknown['change_in_annual_net_gain']),'step_up_share_pct':100*pair/denominator,'movement_share_nonemploying_pct':movement_share,'council_sensitivity':summary},indent=2))
