"""Allocate the existing proposed functions to separate lighting/switching nodes."""
import csv
from collections import Counter, defaultdict
from pathlib import Path

HERE = Path(__file__).resolve().parent
rows = list(csv.DictReader((HERE / 'branch-allocation.csv').open()))
original = list(csv.DictReader((HERE.parent / 'yacht-circuits/circuit-inventory.csv').open()))
source = {r['id']: r for r in original}
zones = defaultdict(list)
result = []
for r in rows:
    item = dict(source_record=r['source_record'], vessel=r['vessel'], pdf_page=r['pdf_page'],
                function=r['function'], protection_A=r['protection_A'], route=r['route'],
                zone=r['proposed_zone'], output_mode=r['output_mode'], module='', channel='',
                target_channel_A='', interface_compatibility='unverified', notes=r['notes'])
    result.append(item)
    if r['route'] == 'core':
        item['route'] = 'specialized_module'
        family = 'Light8' if r['output_mode'] == 'pwm_lighting' else 'Switch16'
        zones[(r['vessel'], r['proposed_zone'], family)].append(item)

budgets = []
for (v, zone, family), items in zones.items():
    if family == 'Light8':
        free = [(f'L{i:02}', 10) for i in range(5, 9)] + [(f'L{i:02}', 20) for i in range(1, 5)]
        capacity, di, total_target = 8, 8, 60
    else:
        free = [(f'S{i:02}', 15) for i in range(5, 17)] + [(f'S{i:02}', 30) for i in range(1, 5)]
        capacity, di, total_target = 16, 16, 100
    module = f'{v}-{zone}-{family}-1'
    assert len(items) < capacity, (module, 'No spare position remains')
    for item in sorted(items, key=lambda x: float(x['protection_A']), reverse=True):
        index = next(i for i, (_, cap) in enumerate(free) if cap >= float(item['protection_A']))
        channel, cap = free.pop(index)
        item.update(module=module, channel=channel, target_channel_A=cap)
    budgets.append(dict(vessel=v, zone=zone, module=family, nodes=1, outputs_used=len(items),
                        outputs_available=capacity, spare_outputs=len(free),
                        spare_high_current_outputs=sum(cap == (20 if family == 'Light8' else 30) for _, cap in free),
                        di_capacity=di, di_used='unresolved', independent_dc_feeds=1,
                        aggregate_current_target_A=total_target, aggregate_qualified='NO'))

def write(name, records):
    with (HERE / name).open('w', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=records[0].keys(), lineterminator="\n")
        writer.writeheader(); writer.writerows(records)

write('modular-branch-allocation.csv', result)
write('modular-zone-budget.csv', budgets)
vessels = []
for v in sorted({r['vessel'] for r in rows}):
    b = [r for r in budgets if r['vessel'] == v]
    counts = Counter(r['module'] for r in b)
    vessels.append(dict(vessel=v, Light8=counts['Light8'], Switch16=counts['Switch16'],
                        used_pwm=sum(r['outputs_used'] for r in b if r['module'] == 'Light8'),
                        used_on_off=sum(r['outputs_used'] for r in b if r['module'] == 'Switch16'),
                        retained_external_paths=sum(r['vessel'] == v and r['route'] == 'external' for r in result),
                        Sense88='sender/zone dependent', Control24='optional automation dependent',
                        scope='same nine design zones; not whole-vessel qualified BOM'))
write('modular-vessel-budget.csv', vessels)

assert len(result) == len({r['source_record'] for r in result}) == 151
assert len(budgets) == 18
assert sum(r['outputs_used'] for r in budgets) == 120
assert sum(r['outputs_available'] for r in budgets) == 216
assert sum(r['route'] == 'external' for r in result) == 31
pins = set()
for r in result:
    assert r['protection_A'] == source[r['source_record']]['protection_A']
    if r['route'] == 'specialized_module':
        pin = (r['module'], r['channel'])
        assert pin not in pins; pins.add(pin)
        assert float(r['protection_A']) <= int(r['target_channel_A'])
        assert ('Light8' in r['module']) == (r['output_mode'] == 'pwm_lighting')
assert sum('Light8' in r['module'] for r in result) == 27
assert sum('Switch16' in r['module'] for r in result) == 93
print('PASS: 151 paths conserved; 27 PWM / 93 ON-OFF / 31 retained; 9 Light8 + 9 Switch16; 216 positions')
for v in vessels: print(v)
