import copy
import csv
from datetime import date
import hashlib
import json
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
EDITION = 'website-nuclear-delivery-2026-09-17'
DATA = ROOT / 'data' / EDITION
RESEARCH = ROOT / 'data/nuclear-delivery-2026-09-17'
REVIEWED = '2026-09-17'
CALCULATIONS = 'nuclear-delivery-calculations'
SELECTED_UNITS = ['FUQING-5', 'BARAKAH-1', 'TAISHAN-1', 'SANMEN-1', 'VOGTLE-3', 'OLKILUOTO-3']
SOURCE_IDS = ['pris-api-CN', 'pris-api-AE', 'pris-api-US', 'pris-api-FI', 'uvek-nuclear-status']
CASE_LABELS = {
    'accelerated_conditional': ('Favourable case', 'Timely decisions, a prepared site and secured suppliers.'),
    'planning_case': ('Planning illustration', 'Longer assessment and construction, with some work in parallel.'),
    'delayed_case': ('Delayed case', 'Legal or licensing delays, followed by difficult construction.'),
}


def load(path):
    return json.loads(path.read_text())


def write(path, value):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(value, indent=2, ensure_ascii=False, allow_nan=False) + '\n')


def artifact(path, download=None):
    content = path.read_bytes()
    row = dict(path=str(path.relative_to(ROOT)), sha256=hashlib.sha256(content).hexdigest(), bytes=len(content))
    if download:
        row['download'] = download
        target = ROOT / 'website/public' / download.lstrip('/')
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_bytes(content)
    return row


def main():
    prior = load(ROOT / 'data/website-evidence-update-2026-09-17/catalog.json')
    catalog = copy.deepcopy(prior)
    originals = {row['source_id']: row for row in load(RESEARCH / 'source-catalog.json')['sources']}
    for source_id in SOURCE_IDS:
        original = originals[source_id]
        raw = artifact(ROOT / original['path'])
        assert raw['sha256'] == original['sha256']
        files = [raw]
        metadata = (ROOT / original['path']).with_suffix('.source.json')
        if metadata.exists():
            files.append(artifact(metadata))
        is_pris = source_id.startswith('pris-')
        catalog['sources'].append(dict(
            id=source_id, title=original['title'], publisher=original['publisher'], kind='primary',
            url=original['url'], publicationDate=original.get('publication_date'),
            edition='Snapshot retrieved 17 September 2026', retrievedAt=original['retrieved_at_utc'], reviewedAt=REVIEWED,
            locator='; '.join(original['reviewed_locators']), artifacts=files,
            note='Original public JSON, archived with an HTML filename suffix. Construction, first grid and commercial-operation dates are separate milestones. Selected completed units illustrate variation; they do not give a Swiss probability forecast.' if is_pris else 'Kernenergie section, checked on 17 September 2026: the ban remains pending the referendum process. The expected 2027 vote is a stated expectation. A successful repeal would still require project-specific approvals.',
        ))

    added = []

    def add(claim_id, label, value, unit, status, period, definition, limitation, sources, formula=None):
        row = dict(id=claim_id, label=label, value=value, unit=unit, status=status, period=period,
                   definition=definition, limitation=limitation, sources=sources, formula=formula, reviewedAt=REVIEWED)
        added.append(row)
        catalog['claims'].append(row)

    references = []
    units = {row['unit']: row for row in load(RESEARCH / 'reference-units.json')}
    countries = {'CN': 'China', 'AE': 'United Arab Emirates', 'US': 'United States', 'FI': 'Finland'}
    for name in SELECTED_UNITS:
        row = copy.deepcopy(units[name])
        raw_units = load(ROOT / originals[row['source_id']]['path'])['items']
        raw = next(item for item in raw_units if item['unitName'] == name)
        for local, original in [('first_concrete_pris', 'constructionDate'), ('first_grid', 'gridDate'), ('commercial_pris', 'commercialDate')]:
            assert row[local] == raw[original][:10]
        years = (date.fromisoformat(row['commercial_pris']) - date.fromisoformat(row['first_concrete_pris'])).days / 365.25
        assert abs(years - row['construction_to_commercial_years']) < .00051
        row['claim_id'] = 'build-years-' + name.lower()
        row['country_label'] = countries[row['country']]
        row['display_name'] = name.title().replace('-', ' ')
        add(row['claim_id'], f'{row["display_name"]}: construction to commercial operation', years, 'years', 'calculated',
            f'{row["first_concrete_pris"]} to {row["commercial_pris"]}',
            'Elapsed time between the IAEA PRIS construction-start and commercial-operation dates.',
            'Earlier planning and licensing are outside this interval. Selected examples span designs and jurisdictions, not a representative statistical sample. Olkiluoto’s operator uses a different regular-production milestone.',
            [row['source_id'], CALCULATIONS], f'({row["commercial_pris"]} − {row["first_concrete_pris"]}) in calendar days / 365.25.')
        row['exact_years'] = years
        references.append(row)

    schedules = []
    for original in load(RESEARCH / 'delivery-scenarios.json'):
        row = copy.deepcopy(original)
        row.pop('source_refs')
        row['label'], row['description'] = CASE_LABELS[row['id']]
        row['grid_year'] = row['first_concrete'] + row['construction_to_grid_years']
        assert row['commercial_year'] == row['grid_year'] + row['commissioning_years']
        row['claim_id'] = 'delivery-' + row['id'].replace('_', '-')
        add(row['claim_id'], row['label'] + ': first commercial operation', row['commercial_year'], 'year', 'scenario',
            'Illustrative first Swiss project; preparation begins in 2026',
            'Analyst schedule combining assumed legal availability, overlapping preparation and approvals, construction and commissioning.',
            'A conditional first-unit case, with no assigned probability. Site approval, supplier slots, finance and legal change remain dependencies. Greater delay or cancellation is possible.',
            [CALCULATIONS], f'{row["first_concrete"]} first concrete + {row["construction_to_grid_years"]} years to first grid + {row["commissioning_years"]} commissioning year = {row["commercial_year"]}. All durations are assumptions.')
        schedules.append(row)

    bridge = load(RESEARCH / 'existing-fleet-bridge.json')
    bridge['source_refs'] = [dict(source_id='nuclear-history-calculation', locator='2021–2025 plant-year generation')]
    annual = load(ROOT / 'data/processed/nuclear-annual-2016-2025.json')
    beznau_rows = [row for row in annual if row['id'] in ['beznau-1', 'beznau-2'] and 2021 <= row['year'] <= 2025]
    assert len(beznau_rows) == 10
    mean = sum(row['net_generation_gwh'] for row in beznau_rows) / 5 / 1000
    assert abs(mean - bridge['beznau_combined_mean_twh']) < 1e-10
    add('beznau-mean-2021-2025', 'Beznau’s average annual electricity', mean, 'TWh/year', 'calculated', '2021–2025',
        'Combined net output of Beznau 1 and 2, averaged over five years, including outages.',
        'Historical annual energy sets the scale. Replacement supply must also meet winter and hourly needs; the average is not guaranteed future output.',
        ['nuclear-history-calculation', CALCULATIONS], 'Sum the ten plant-year net-generation values in GWh; divide by five years and by 1,000.')
    lifetimes = next(row for row in load(RESEARCH / 'swiss-delivery-evidence.json') if row['id'] == 'existing-lifetimes')['value']
    for unit in [2, 1]:
        year = lifetimes[f'beznau{unit}_retirement_intention']
        add(f'beznau-{unit}-retirement', f'Beznau {unit}: announced retirement year', year, 'year', 'scenario', 'Operator intention announced December 2024',
            'Axpo’s announced operating horizon, recorded in ENSI’s tenth national report, introduction.',
            'An operator intention, distinct from a fixed statutory lifetime. Continued operation remains subject to safety requirements and the owner’s decision.',
            ['ensi-cns-2025'], 'Use the operator intention recorded by ENSI; no lifetime extrapolation.')
    add('nuclear-referendum-expected', 'Expected vote on the new-build ban', 2027, 'year', 'scenario', 'DETEC position checked 17 September 2026',
        'DETEC expects the referendum vote in 2027 after parliamentary approval of repeal.',
        'Expected timing and outcome remain conditional. Repeal would allow project applications; it would not approve a station.',
        ['uvek-nuclear-status'], 'Directly reproduce DETEC’s stated expected vote year; no probability assigned.')

    dataset = dict(reviewedAt=REVIEWED, references=references, schedules=schedules, bridge=bridge,
                   retirement_intentions={f'beznau-{unit}': lifetimes[f'beznau{unit}_retirement_intention'] for unit in [1, 2]},
                   claims=added,
                   selection='Six completed reference units span four designs and four countries, including different outcomes within AP1000 and EPR designs. This is an illustrative selection, not a global sample or estimate of Swiss completion odds.',
                   inputs=[artifact(RESEARCH / name) for name in ['reference-units.json', 'delivery-scenarios.json', 'existing-fleet-bridge.json', 'swiss-delivery-evidence.json', 'source-catalog.json']],
                   recommendations=['Begin owner-led assessment of mature large-reactor designs.', 'Procure the Beznau replacement bridge independently of new nuclear.', 'Keep SMRs as a conditional option until reference performance and executable offers support investment.', 'Make further units depend on site, grid, supplier and financing capacity; do not assume a fleet delivery rate.'])
    write(DATA / 'evidence.json', dataset)
    files = [artifact(DATA / 'evidence.json', '/downloads/nuclear-delivery/evidence.json'), artifact(Path(__file__).resolve(), '/downloads/nuclear-delivery/build_website_nuclear_delivery.py')]
    for name, rows, fields in [
        ('reference-builds.csv', references, ['unit', 'design', 'country_label', 'first_concrete_pris', 'first_grid', 'commercial_pris', 'exact_years', 'source_id', 'locator']),
        ('delivery-scenarios.csv', schedules, ['id', 'preparation_start', 'law_available', 'general_licence_start', 'general_licence_end', 'construction_licence_start', 'construction_licence_end', 'first_concrete', 'grid_year', 'commercial_year', 'note']),
    ]:
        with (DATA / name).open('w', newline='') as stream:
            writer = csv.DictWriter(stream, fieldnames=fields, extrasaction='ignore')
            writer.writeheader()
            writer.writerows(rows)
        files.append(artifact(DATA / name, '/downloads/nuclear-delivery/' + name))
    catalog['sources'].append(dict(id=CALCULATIONS, title='Nuclear construction records and conditional Swiss delivery cases', publisher='WattInfo', kind='project-calculation',
        url='/downloads/nuclear-delivery/evidence.json', publicationDate=REVIEWED, edition='Delivery evidence, 17 September 2026', retrievedAt=REVIEWED, reviewedAt=REVIEWED,
        locator='references; schedules; bridge; claims; linked primary source records',
        note='Construction intervals are checked against original PRIS dates. First Swiss operation in 2042, 2047 or 2053 follows explicit analyst durations, without probabilities. Beznau’s historical average uses 2021–2025 net output. Full third-party originals remain outside the public downloads.', artifacts=files))
    for collection in ['claims', 'sources']:
        assert catalog[collection][:len(prior[collection])] == prior[collection]
    catalog['reviewedAt'] = REVIEWED
    for path in [DATA / 'catalog.json', ROOT / 'website/src/data/catalog.json', ROOT / 'website/public/downloads/catalog.json']:
        write(path, catalog)
    print(f'Added {len(SOURCE_IDS)} primary sources, one calculation record and {len(added)} claims.')


if __name__ == '__main__':
    main()
