from pathlib import Path
from fractions import Fraction
import hashlib
import json
import re

ROOT = Path('/workspace/scratch/1b40da62dcbd')
OUT = ROOT / 'c532_c631/prep'
P68 = ROOT / 'project_sources/08-File_68.Enochic_Seven_Week_Core-20260819-145634-.md'
P69 = ROOT / 'project_sources/11-File_69.Enoch_Centered_Genealogical_Accordion-20260823-145801-.md'
S68, S69 = P68.read_text(), P69.read_text()

def section(text, start, end):
    return text.split(start, 1)[1].split(end, 1)[0]

def cells(line):
    return [p.strip().replace('`', '') for p in line.strip().strip('|').split('|')]

def point(text, default='BC'):
    number = int(re.search(r'\d+', text).group())
    era = 'AD' if 'AD' in text else ('BC' if 'BC' in text else default)
    return dict(label=f'{number} BC' if era == 'BC' else f'AD {number}',
                a=1-number if era == 'BC' else number)

def interval(text):
    left, right = text.split('–')
    default = 'AD' if 'AD' in right else 'BC'
    return dict(source=text, start=point(left, default), end=point(right, default))

rail_specs = [('primary_original', 70, -110), ('primary_translated', 70, 0),
              ('companion_original', 72, -110), ('companion_translated', 72, 0)]
grid = []
for line in section(S68, '### C.4 Week 1 generation rows', '### C.11 Retained / expunged attribution overlay').splitlines():
    c = cells(line)
    if len(c) == 7 and c[0].isdigit() and c[1].isdigit():
        j = len(grid)
        grid.append(dict(j=j, day=int(c[0]), source_generation_count=int(c[1]),
                         retained_occupant=c[2], rails={name:interval(c[i+3])
                         for i, (name, _, _) in enumerate(rail_specs)}))
assert len(grid) == 49

overlay = []
for line in section(S68, '### C.11 Retained / expunged attribution overlay', '### C.12 Attribution and row-control conclusion').splitlines():
    c = cells(line)
    if len(c) == 8 and c[0].startswith('W') and c[1].isdigit():
        overlay.append(dict(position=c[0], source_generation_count=int(c[1]), retained=c[2],
                            expunged=c[3], heads={name:point(c[i+4])
                            for i, (name, _, _) in enumerate(rail_specs)}))
assert len(overlay) == 18

master = []
parts = [
    ('### B.1 Primary generation resolution `180`', '### B.2', 70, 180, 'generation'),
    ('### B.2 Companion generation resolution `182`', '### B.3', 72, 182, 'generation'),
    ('### B.3 Primary ordinary carrier `70`', '### B.4 ', 70, 70, 'ordinary'),
    ('### B.4 Companion ordinary carrier `72`', '### B.4A', 72, 72, 'ordinary')]
roles = ['birth_plus_110', 'birth_no_rail', 'ascension_plus_110', 'ascension_no_rail']
counts = dict(Luke=77, Matthew=63, Enochic=50)
for heading, end, g, u, resolution in parts:
    for line in section(S69, heading, end).splitlines():
        c = cells(line)
        if len(c) == 5 and c[0] in counts:
            master.append(dict(schematic=c[0], N=counts[c[0]], g=g, u=u, resolution=resolution,
                               role_order=roles,
                               source_BC={role:int(re.search(r'\d+', value).group())
                                          for role, value in zip(roles,c[1:])}))
assert len(master) == 12

weekly = []
for line in section(S68, '### J.2 Full-resolution weekly boundary table', '### J.3 Weekly gap-contraction table').splitlines():
    c = cells(line)
    if len(c) == 5 and c[0].isdigit():
        weekly.append(dict(k=int(c[0]), primary=point(c[1]), companion=point(c[2]),
                           gap=int(c[3]), function=c[4]))
assert len(weekly) == 8

week_cells = []
for line in section(S69, '### B.5 Primary week resolution `600`', '### B.6 Companion weekly overlays').splitlines():
    c = cells(line)
    if len(c) == 5 and c[0] in counts:
        week_cells.append(dict(schematic=c[0], g=70, week_diagonal=600,
                               source_BC={role:int(re.search(r'\d+', value).group())
                                          for role,value in zip(roles,c[1:])}))
assert len(week_cells) == 3

# Validate extracted source tables from one declared parameter basis.
for row in grid:
    j = row['j']
    assert row['day'] == j % 7 + 1
    assert row['source_generation_count'] == 49-j
    for name,g,rail_shift in rail_specs:
        source = row['rails'][name]
        assert source['start']['a'] == 65-(49-j)*g+rail_shift
        assert source['end']['a'] == 65-(48-j)*g+rail_shift
for row in overlay:
    j = 49-row['source_generation_count']
    for name,_,_ in rail_specs:
        assert row['heads'][name]['a'] == grid[j]['rails'][name]['start']['a']
for row in master:
    x = (row['N']-7)*row['u']-64
    predicted = dict(zip(roles, [x+110,x,x+110-row['g'],x-row['g']]))
    assert row['source_BC'] == predicted
for row in weekly:
    k = row['k']
    assert row['primary']['a'] == 65-(49-7*k)*180
    assert row['companion']['a'] == 65-(49-7*k)*182
    assert row['gap'] == 98-14*k
for row in week_cells:
    n = {'Luke':10,'Matthew':8,'Enochic':6}[row['schematic']]
    residual = 70 if row['schematic'] == 'Enochic' else 0
    x = 600*n+residual-64
    assert row['source_BC'] == dict(zip(roles,[x+110,x,x+40,x-70]))

centers = []
for g,u in [(70,70),(72,72),(70,180),(72,182)]:
    L,M,Q = [n*u-64 for n in (70,56,43)]
    ordinary_actual_midpoint=Fraction(L+Q-g,2)
    crossed_actual_midpoint=Fraction(L-g+Q+110,2)
    centers.append(dict(g=g,u=u,
                        same_rail_triple_BC=[L,M,Q-g],
                        same_rail_midpoint_residual_from_M=str(ordinary_actual_midpoint-M),
                        crossed_min_triple_BC=[L-g,M+110,Q+110],
                        crossed_max_triple_BC=[L+110,M+110,Q-g],
                        crossed_midpoint_residual_from_M_plus_T=str(crossed_actual_midpoint-(M+110))))
    assert ordinary_actual_midpoint-M == Fraction(u-g,2)
    assert crossed_actual_midpoint-(M+110) == Fraction(u-g-110,2)

packet = dict(
    scope='Source preparation only; no numbered research step completed.',
    source_files=[dict(path=str(p), sha256=hashlib.sha256(p.read_bytes()).hexdigest()) for p in (P68,P69)],
    coordinate='Civil astronomical a(BC B)=1-B, a(AD A)=A. No civil year zero is displayed.',
    parameter_basis=dict(terminal_a=65, ordinary_carriers=[70,72], rail=110,
                         generation_resolutions=[180,182], full_outer_counts=counts),
    file68_four_rail_generation_rows=grid,
    file68_fixed_coordinate_attribution_overlay=overlay,
    file69_complete_Enoch_cells=master,
    file68_full_resolution_week_boundaries=weekly,
    file69_primary_week_cells=week_cells,
    file69_bounded_week_overlays=[
        dict(name='Lukan transported-primary cell',primary_BC=[5936,5866],
             transported_BC=[6076,6006],shift_BC=140,local_gap=70,
             source='File69 §4.12',strict_72_counterfactual_close_BC=6004),
        dict(name='Enochic twenty-cell field',primary_BC=[3716,3606,3646,3536],
             transported_BC=[3756,3646,3686,3576],shift_BC=40,local_gap=70,
             source='File69 §4.13')],
    center_role_diagnostics=centers,
    internal_external_counts=[dict(u=u,external=[14*u,13*u],internal=[13*u,14*u],
                                  core=27*u,completed_count_volume=28*u) for u in (70,72,180,182)],
    resolutions=[dict(g=g,field=49*g+110,week_sum=7*(7*g+110),
                      generation_sum=49*(g+110),outer_generation=50*(g+110),
                      diagonal=7*g+110,duplicated_overlap=6*110)
                 for g in (70,72)])
OUT.mkdir(parents=True,exist_ok=True)
(OUT/'file68_69_source_tables.json').write_text(json.dumps(packet,indent=2,ensure_ascii=False)+'\n')
print(json.dumps(dict(status='pass',generation_rows=len(grid),rail_interval_cells=len(grid)*4,
                     attribution_rows=len(overlay),master_cells=len(master),master_coordinates=len(master)*4,
                     full_resolution_boundaries=len(weekly),output=str(OUT/'file68_69_source_tables.json'))))
