"""Periodic Table Discovery — All 118 elements mapped through C=0

Maps every element (name + symbol + atomic number) through flip().
Tests physics: gravitational interaction, resonance, Hamiltonian energy.
Discovers: do element groups cluster? Do periods align?

No training. No chemistry database. The coordinate IS the element.
"""
from cubesquare.shells.flip import flip
from cubesquare.shells.world import get_body_state, compute_interaction
from cubesquare.shells.gravity import compute_gravitational_field
import json
import math
import time

# ═══════════════════════════════════════════════════════════════════
# COMPLETE PERIODIC TABLE — 118 elements
# (atomic_number, symbol, name, group, period, category)
# ═══════════════════════════════════════════════════════════════════

ELEMENTS = [
    (1, 'H', 'hydrogen', 1, 1, 'nonmetal'),
    (2, 'He', 'helium', 18, 1, 'noble gas'),
    (3, 'Li', 'lithium', 1, 2, 'alkali metal'),
    (4, 'Be', 'beryllium', 2, 2, 'alkaline earth'),
    (5, 'B', 'boron', 13, 2, 'metalloid'),
    (6, 'C', 'carbon', 14, 2, 'nonmetal'),
    (7, 'N', 'nitrogen', 15, 2, 'nonmetal'),
    (8, 'O', 'oxygen', 16, 2, 'nonmetal'),
    (9, 'F', 'fluorine', 17, 2, 'halogen'),
    (10, 'Ne', 'neon', 18, 2, 'noble gas'),
    (11, 'Na', 'sodium', 1, 3, 'alkali metal'),
    (12, 'Mg', 'magnesium', 2, 3, 'alkaline earth'),
    (13, 'Al', 'aluminium', 13, 3, 'post-transition'),
    (14, 'Si', 'silicon', 14, 3, 'metalloid'),
    (15, 'P', 'phosphorus', 15, 3, 'nonmetal'),
    (16, 'S', 'sulfur', 16, 3, 'nonmetal'),
    (17, 'Cl', 'chlorine', 17, 3, 'halogen'),
    (18, 'Ar', 'argon', 18, 3, 'noble gas'),
    (19, 'K', 'potassium', 1, 4, 'alkali metal'),
    (20, 'Ca', 'calcium', 2, 4, 'alkaline earth'),
    (21, 'Sc', 'scandium', 3, 4, 'transition metal'),
    (22, 'Ti', 'titanium', 4, 4, 'transition metal'),
    (23, 'V', 'vanadium', 5, 4, 'transition metal'),
    (24, 'Cr', 'chromium', 6, 4, 'transition metal'),
    (25, 'Mn', 'manganese', 7, 4, 'transition metal'),
    (26, 'Fe', 'iron', 8, 4, 'transition metal'),
    (27, 'Co', 'cobalt', 9, 4, 'transition metal'),
    (28, 'Ni', 'nickel', 10, 4, 'transition metal'),
    (29, 'Cu', 'copper', 11, 4, 'transition metal'),
    (30, 'Zn', 'zinc', 12, 4, 'transition metal'),
    (31, 'Ga', 'gallium', 13, 4, 'post-transition'),
    (32, 'Ge', 'germanium', 14, 4, 'metalloid'),
    (33, 'As', 'arsenic', 15, 4, 'metalloid'),
    (34, 'Se', 'selenium', 16, 4, 'nonmetal'),
    (35, 'Br', 'bromine', 17, 4, 'halogen'),
    (36, 'Kr', 'krypton', 18, 4, 'noble gas'),
    (37, 'Rb', 'rubidium', 1, 5, 'alkali metal'),
    (38, 'Sr', 'strontium', 2, 5, 'alkaline earth'),
    (39, 'Y', 'yttrium', 3, 5, 'transition metal'),
    (40, 'Zr', 'zirconium', 4, 5, 'transition metal'),
    (41, 'Nb', 'niobium', 5, 5, 'transition metal'),
    (42, 'Mo', 'molybdenum', 6, 5, 'transition metal'),
    (43, 'Tc', 'technetium', 7, 5, 'transition metal'),
    (44, 'Ru', 'ruthenium', 8, 5, 'transition metal'),
    (45, 'Rh', 'rhodium', 9, 5, 'transition metal'),
    (46, 'Pd', 'palladium', 10, 5, 'transition metal'),
    (47, 'Ag', 'silver', 11, 5, 'transition metal'),
    (48, 'Cd', 'cadmium', 12, 5, 'transition metal'),
    (49, 'In', 'indium', 13, 5, 'post-transition'),
    (50, 'Sn', 'tin', 14, 5, 'post-transition'),
    (51, 'Sb', 'antimony', 15, 5, 'metalloid'),
    (52, 'Te', 'tellurium', 16, 5, 'metalloid'),
    (53, 'I', 'iodine', 17, 5, 'halogen'),
    (54, 'Xe', 'xenon', 18, 5, 'noble gas'),
    (55, 'Cs', 'caesium', 1, 6, 'alkali metal'),
    (56, 'Ba', 'barium', 2, 6, 'alkaline earth'),
    (57, 'La', 'lanthanum', 3, 6, 'lanthanide'),
    (58, 'Ce', 'cerium', 3, 6, 'lanthanide'),
    (59, 'Pr', 'praseodymium', 3, 6, 'lanthanide'),
    (60, 'Nd', 'neodymium', 3, 6, 'lanthanide'),
    (61, 'Pm', 'promethium', 3, 6, 'lanthanide'),
    (62, 'Sm', 'samarium', 3, 6, 'lanthanide'),
    (63, 'Eu', 'europium', 3, 6, 'lanthanide'),
    (64, 'Gd', 'gadolinium', 3, 6, 'lanthanide'),
    (65, 'Tb', 'terbium', 3, 6, 'lanthanide'),
    (66, 'Dy', 'dysprosium', 3, 6, 'lanthanide'),
    (67, 'Ho', 'holmium', 3, 6, 'lanthanide'),
    (68, 'Er', 'erbium', 3, 6, 'lanthanide'),
    (69, 'Tm', 'thulium', 3, 6, 'lanthanide'),
    (70, 'Yb', 'ytterbium', 3, 6, 'lanthanide'),
    (71, 'Lu', 'lutetium', 3, 6, 'lanthanide'),
    (72, 'Hf', 'hafnium', 4, 6, 'transition metal'),
    (73, 'Ta', 'tantalum', 5, 6, 'transition metal'),
    (74, 'W', 'tungsten', 6, 6, 'transition metal'),
    (75, 'Re', 'rhenium', 7, 6, 'transition metal'),
    (76, 'Os', 'osmium', 8, 6, 'transition metal'),
    (77, 'Ir', 'iridium', 9, 6, 'transition metal'),
    (78, 'Pt', 'platinum', 10, 6, 'transition metal'),
    (79, 'Au', 'gold', 11, 6, 'transition metal'),
    (80, 'Hg', 'mercury', 12, 6, 'transition metal'),
    (81, 'Tl', 'thallium', 13, 6, 'post-transition'),
    (82, 'Pb', 'lead', 14, 6, 'post-transition'),
    (83, 'Bi', 'bismuth', 15, 6, 'post-transition'),
    (84, 'Po', 'polonium', 16, 6, 'post-transition'),
    (85, 'At', 'astatine', 17, 6, 'halogen'),
    (86, 'Rn', 'radon', 18, 6, 'noble gas'),
    (87, 'Fr', 'francium', 1, 7, 'alkali metal'),
    (88, 'Ra', 'radium', 2, 7, 'alkaline earth'),
    (89, 'Ac', 'actinium', 3, 7, 'actinide'),
    (90, 'Th', 'thorium', 3, 7, 'actinide'),
    (91, 'Pa', 'protactinium', 3, 7, 'actinide'),
    (92, 'U', 'uranium', 3, 7, 'actinide'),
    (93, 'Np', 'neptunium', 3, 7, 'actinide'),
    (94, 'Pu', 'plutonium', 3, 7, 'actinide'),
    (95, 'Am', 'americium', 3, 7, 'actinide'),
    (96, 'Cm', 'curium', 3, 7, 'actinide'),
    (97, 'Bk', 'berkelium', 3, 7, 'actinide'),
    (98, 'Cf', 'californium', 3, 7, 'actinide'),
    (99, 'Es', 'einsteinium', 3, 7, 'actinide'),
    (100, 'Fm', 'fermium', 3, 7, 'actinide'),
    (101, 'Md', 'mendelevium', 3, 7, 'actinide'),
    (102, 'No', 'nobelium', 3, 7, 'actinide'),
    (103, 'Lr', 'lawrencium', 3, 7, 'actinide'),
    (104, 'Rf', 'rutherfordium', 4, 7, 'transition metal'),
    (105, 'Db', 'dubnium', 5, 7, 'transition metal'),
    (106, 'Sg', 'seaborgium', 6, 7, 'transition metal'),
    (107, 'Bh', 'bohrium', 7, 7, 'transition metal'),
    (108, 'Hs', 'hassium', 8, 7, 'transition metal'),
    (109, 'Mt', 'meitnerium', 9, 7, 'transition metal'),
    (110, 'Ds', 'darmstadtium', 10, 7, 'transition metal'),
    (111, 'Rg', 'roentgenium', 11, 7, 'transition metal'),
    (112, 'Cn', 'copernicium', 12, 7, 'transition metal'),
    (113, 'Nh', 'nihonium', 13, 7, 'post-transition'),
    (114, 'Fl', 'flerovium', 14, 7, 'post-transition'),
    (115, 'Mc', 'moscovium', 15, 7, 'post-transition'),
    (116, 'Lv', 'livermorium', 16, 7, 'post-transition'),
    (117, 'Ts', 'tennessine', 17, 7, 'halogen'),
    (118, 'Og', 'oganesson', 18, 7, 'noble gas'),
]

LAYER_NAMES = {
    1: 'Geometric', 2: 'Semantic', 3: 'Spatial', 4: 'Functional',
    5: 'Probabilistic', 6: 'Amotional', 7: 'Frequency',
    8: 'Electromagnetic', 9: 'Membrane',
}


def map_all_elements():
    """Map every element name through flip. Return structured results."""
    results = []
    for z, sym, name, group, period, cat in ELEMENTS:
        r = flip(name, 'text')
        r_sym = flip(sym, 'text')
        results.append({
            'z': z,
            'symbol': sym,
            'name': name,
            'group': group,
            'period': period,
            'category': cat,
            'name_hex': r.hex_id,
            'name_basin': r.basin,
            'name_layer': r.layer,
            'sym_hex': r_sym.hex_id,
            'sym_basin': r_sym.basin,
            'sym_layer': r_sym.layer,
            'name_sym_distance': abs(r.basin - r_sym.basin),
            'same_layer': r.layer == r_sym.layer,
        })
    return results


def test_physics(elements):
    """Test gravitational interaction and Hamiltonian energy for key elements."""
    # Pick representative elements for physics tests
    test_pairs = [
        ('hydrogen', 'oxygen'),     # H2O components
        ('sodium', 'chlorine'),     # NaCl ionic bond
        ('iron', 'carbon'),         # steel alloy
        ('gold', 'silver'),         # precious metals
        ('uranium', 'plutonium'),   # actinides
        ('helium', 'neon'),         # noble gases
        ('lithium', 'fluorine'),    # LiF ionic
        ('nitrogen', 'oxygen'),     # atmosphere
    ]

    physics_results = []
    for name_a, name_b in test_pairs:
        ea = next((e for e in elements if e['name'] == name_a), None)
        eb = next((e for e in elements if e['name'] == name_b), None)
        if ea and eb:
            # Get body states
            body_a = get_body_state(ea['name_hex'])
            body_b = get_body_state(eb['name_hex'])

            # Compute interaction
            interaction = compute_interaction(ea['name_hex'], eb['name_hex'])

            # Gravitational fields
            grav_a = compute_gravitational_field(ea['name_hex'])
            grav_b = compute_gravitational_field(eb['name_hex'])

            physics_results.append({
                'pair': f"{name_a}-{name_b}",
                'distance': interaction['distance']['value'],
                'srl_hops': interaction['distance']['srl_hops'],
                'force': interaction['force']['gravitational'],
                'resonance': interaction['resonance']['type'],
                'resonance_factor': interaction['resonance']['factor'],
                'i_states': interaction['resonance']['i_states'],
                'membrane': interaction['membrane']['interaction'],
                'basins': interaction['membrane']['basins'],
                'energy_a': body_a['energy']['kinetic'],
                'energy_b': body_b['energy']['kinetic'],
                'mass_a': body_a['mass']['position_mass'],
                'mass_b': body_b['mass']['position_mass'],
                'hamiltonian_a': body_a['energy']['kinetic'] + grav_a['gravitational_potential'],
                'hamiltonian_b': body_b['energy']['kinetic'] + grav_b['gravitational_potential'],
            })

    return physics_results


def analyze_clustering(elements):
    """Analyze: do chemical categories cluster in the same layers?"""
    category_layers = {}
    for e in elements:
        cat = e['category']
        if cat not in category_layers:
            category_layers[cat] = {}
        layer = e['name_layer']
        category_layers[cat][layer] = category_layers[cat].get(layer, 0) + 1

    return category_layers


def analyze_groups(elements):
    """Analyze: do periodic table groups share layers?"""
    group_layers = {}
    for e in elements:
        g = e['group']
        if g not in group_layers:
            group_layers[g] = {}
        layer = e['name_layer']
        group_layers[g][layer] = group_layers[g].get(layer, 0) + 1

    return group_layers


def run_periodic_table():
    """Full periodic table discovery and physics test."""
    t0 = time.time()

    # 1. Map all elements
    print("\n" + "=" * 80)
    print("PERIODIC TABLE DISCOVERY — 118 elements through C=0")
    print("=" * 80)

    elements = map_all_elements()
    t_map = time.time() - t0

    # Print the map
    print(f"\n{'Z':>3} {'Sym':<4} {'Name':<16} {'Grp':>3} {'Per':>3} "
          f"{'Category':<18} {'Hex':<10} {'Basin':>5} {'Layer':<16} "
          f"{'SymHex':<10} {'SBas':>4} {'SL':>2} {'Dist':>4} {'=L?'}")
    print("-" * 120)

    for e in elements:
        ln = LAYER_NAMES.get(e['name_layer'], f"L{e['name_layer']}")
        eq = "YES" if e['same_layer'] else ""
        print(f"{e['z']:>3} {e['symbol']:<4} {e['name']:<16} {e['group']:>3} "
              f"{e['period']:>3} {e['category']:<18} {e['name_hex']:<10} "
              f"B{e['name_basin']:03d} L{e['name_layer']} {ln:<12} "
              f"{e['sym_hex']:<10} B{e['sym_basin']:03d} L{e['sym_layer']:>1} "
              f"{e['name_sym_distance']:>4} {eq}")

    # 2. Layer distribution
    print(f"\n{'='*80}")
    print("LAYER DISTRIBUTION — Where do elements live?")
    print("=" * 80)

    layer_counts = {}
    for e in elements:
        l = e['name_layer']
        layer_counts[l] = layer_counts.get(l, 0) + 1

    for l in sorted(layer_counts.keys()):
        ln = LAYER_NAMES.get(l, f"L{l}")
        count = layer_counts[l]
        bar = "#" * count
        names = [e['symbol'] for e in elements if e['name_layer'] == l]
        print(f"  L{l} {ln:<16} [{count:>3}] {bar}")
        # Show which elements
        for i in range(0, len(names), 20):
            chunk = names[i:i+20]
            print(f"     {' '.join(chunk)}")

    # 3. Category clustering
    print(f"\n{'='*80}")
    print("CATEGORY CLUSTERING — Do chemical families stay together?")
    print("=" * 80)

    cat_layers = analyze_clustering(elements)
    for cat in sorted(cat_layers.keys()):
        dist = cat_layers[cat]
        total = sum(dist.values())
        dominant = max(dist, key=dist.get)
        dominant_pct = dist[dominant] / total * 100
        layers_str = ", ".join(f"L{l}:{n}" for l, n in sorted(dist.items()))
        print(f"  {cat:<20} [{total:>2} elements] dominant=L{dominant} "
              f"({dominant_pct:.0f}%) | {layers_str}")

    # 4. Name-Symbol agreement
    print(f"\n{'='*80}")
    print("NAME vs SYMBOL — Do they land in the same layer?")
    print("=" * 80)

    same_count = sum(1 for e in elements if e['same_layer'])
    diff_count = len(elements) - same_count
    print(f"  Same layer: {same_count}/{len(elements)} ({same_count/len(elements)*100:.1f}%)")
    print(f"  Different:  {diff_count}/{len(elements)} ({diff_count/len(elements)*100:.1f}%)")

    # Closest name-symbol pairs
    closest = sorted(elements, key=lambda e: e['name_sym_distance'])[:10]
    print(f"\n  Closest name-symbol pairs:")
    for e in closest:
        sl = "SAME" if e['same_layer'] else f"L{e['name_layer']}->L{e['sym_layer']}"
        print(f"    {e['name']:<16} <-> {e['symbol']:<4}  dist={e['name_sym_distance']:>3}  {sl}")

    # 5. Physics tests
    print(f"\n{'='*80}")
    print("PHYSICS VALIDATION — Hamiltonian + Newtonian on element pairs")
    print("=" * 80)

    physics = test_physics(elements)
    print(f"\n  {'Pair':<22} {'Dist':>6} {'Hops':>4} {'Force':>12} "
          f"{'Resonance':<15} {'Membrane':<12} {'H(A)':>10} {'H(B)':>10}")
    print("  " + "-" * 100)

    for p in physics:
        print(f"  {p['pair']:<22} {p['distance']:>6.1f} {p['srl_hops']:>4} "
              f"{p['force']:>12.6f} {p['resonance']:<15} {p['membrane']:<12} "
              f"{p['hamiltonian_a']:>10.4f} {p['hamiltonian_b']:>10.4f}")

    # 6. Conservation check — does total Hamiltonian make sense?
    print(f"\n  Hamiltonian Conservation Check:")
    for p in physics:
        h_total = p['hamiltonian_a'] + p['hamiltonian_b']
        # After interaction, energy should redistribute but total conserve
        # E_total = T + V (kinetic + potential)
        e_total = (p['energy_a'] + p['energy_b'])
        print(f"    {p['pair']:<22} H_total={h_total:>10.4f}  "
              f"E_kinetic_total={e_total:>10.4f}  "
              f"F={p['force']:.6f}  "
              f"resonance={p['resonance']}")

    # 7. Resonance patterns — which element pairs are constructive?
    print(f"\n{'='*80}")
    print("RESONANCE PATTERNS — I-Logic between elements")
    print("=" * 80)

    constructive = [p for p in physics if p['resonance'] == 'CONSTRUCTIVE']
    complementary = [p for p in physics if p['resonance'] == 'COMPLEMENTARY']
    destructive = [p for p in physics if p['resonance'] == 'DESTRUCTIVE']

    print(f"  CONSTRUCTIVE (same I-Logic state): {len(constructive)}")
    for p in constructive:
        print(f"    {p['pair']}: {p['i_states']}")
    print(f"  COMPLEMENTARY (balanced states): {len(complementary)}")
    for p in complementary:
        print(f"    {p['pair']}: {p['i_states']}")
    print(f"  DESTRUCTIVE (opposing): {len(destructive)}")
    for p in destructive:
        print(f"    {p['pair']}: {p['i_states']}")

    t_total = time.time() - t0
    print(f"\n  Mapped {len(elements)} elements + {len(physics)} physics tests "
          f"in {t_total:.2f}s")

    # Save full results
    output = {
        'elements': elements,
        'physics': physics,
        'layer_distribution': {str(k): v for k, v in layer_counts.items()},
        'category_clustering': {k: {str(lk): lv for lk, lv in v.items()}
                                for k, v in cat_layers.items()},
        'same_layer_count': same_count,
        'total_elements': len(elements),
        'timing_seconds': round(t_total, 3),
    }

    out_path = 'agent_sandbox/periodic_table_map.json'
    with open(out_path, 'w') as f:
        json.dump(output, f, indent=2)
    print(f"  Full data saved to {out_path}")

    return output


if __name__ == '__main__':
    run_periodic_table()
