"""Cross-block constant registry for VMS CAS Audit.

Blocks register their numeric constants here.
The master runner checks global consistency after all blocks complete.
"""

import math

_registry = {}  # canonical_name -> list of (value, source)


def register(name, value, source):
    """Register a constant value from a block.

    Args:
        name: canonical name (e.g., 'c', 'h', 'eps0')
        value: numeric value in SI units
        source: source block identifier (e.g., 'F0007')
    """
    # Safe cast: handle SymPy objects that need .evalf() before float()
    v = float(value.evalf()) if hasattr(value, "evalf") else float(value)
    _registry.setdefault(name, []).append((v, source))


def get(name):
    """Retrieve most recently registered value for a constant."""
    if name not in _registry or not _registry[name]:
        raise KeyError(f"Constant '{name}' not registered")
    return _registry[name][-1][0]


def get_registry():
    """Return the full registry."""
    return _registry


def check_consistency(tol_map=None):
    """Check all registered constants for cross-block consistency.

    Returns (all_pass, report_lines).
    """
    default_tol = 1e-6
    if tol_map is None:
        tol_map = {
            'c': default_tol, 'eps0': default_tol, 'mu0': default_tol,
            'h': default_tol, 'hbar': default_tol, 'kB': default_tol,
            'NA': default_tol, 'sigma_sb': default_tol,
            'G': 1e-4, 'e': default_tol, 'me': 1e-6,
        }

    all_pass = True
    lines = []

    for name in sorted(_registry.keys()):
        entries = _registry[name]
        vals = [v for v, _ in entries]
        srcs = [s for _, s in entries]
        tol = tol_map.get(name, default_tol)

        if len(vals) < 2:
            lines.append(f"  {name:10s} : SKIP (found in {len(vals)} block)")
            continue

        ref = sorted(vals)[len(vals) // 2]  # median
        max_dev = max(abs(v - ref) / max(abs(ref), 1e-300) for v in vals)
        ok = max_dev <= tol
        all_pass = all_pass and ok
        tag = "PASS" if ok else "FAIL"
        lines.append(f"  {name:10s} : {tag}  (n={len(vals)}, max rel dev={max_dev:.3g}, tol={tol:.3g})")

        if not ok:
            for v, s in zip(vals, srcs):
                lines.append(f"    {s:30s}  {v:.15g}")

    return all_pass, lines


def check_identities():
    """Check cross-block identities: c^2*eps0*mu0=1, hbar=h/2pi, sigma formula.

    Returns (all_pass, report_lines).
    """
    all_pass = True
    lines = []
    tol = 1e-6

    # Group constants by source block
    block_consts = {}
    for name, entries in _registry.items():
        for val, src in entries:
            block_consts.setdefault(src, {})[name] = val

    # 1) c^2 * eps0 * mu0 = 1
    id1_pass = True
    id1_any = False
    for src, consts in block_consts.items():
        if 'c' in consts and 'eps0' in consts and 'mu0' in consts:
            id1_any = True
            lhs = consts['c'] ** 2 * consts['eps0'] * consts['mu0']
            if abs(lhs - 1) > tol:
                id1_pass = False
    tag = "PASS" if id1_pass else "FAIL"
    lines.append(f"  c^2*eps0*mu0=1 : {tag}" if id1_any else "  c^2*eps0*mu0=1 : SKIP")
    all_pass = all_pass and (id1_pass or not id1_any)

    # 2) hbar = h / (2*pi)
    id2_pass = True
    id2_any = False
    for src, consts in block_consts.items():
        if 'h' in consts and 'hbar' in consts:
            id2_any = True
            rel = abs(consts['hbar'] - consts['h'] / (2 * math.pi))
            rel /= max(abs(consts['h'] / (2 * math.pi)), 1e-300)
            if rel > tol:
                id2_pass = False
    tag = "PASS" if id2_pass else "FAIL"
    lines.append(f"  hbar=h/(2pi)   : {tag}" if id2_any else "  hbar=h/(2pi)   : SKIP")
    all_pass = all_pass and (id2_pass or not id2_any)

    # 3) sigma_sb = pi^2 * kB^4 / (60 * hbar^3 * c^2)
    id3_pass = True
    id3_any = False
    for src, consts in block_consts.items():
        if all(k in consts for k in ('sigma_sb', 'kB', 'hbar', 'c')):
            id3_any = True
            sig = consts['sigma_sb']
            formula = (math.pi ** 2 * consts['kB'] ** 4) / \
                      (60 * consts['hbar'] ** 3 * consts['c'] ** 2)
            rel = abs(sig - formula) / max(abs(formula), 1e-300)
            # Looser tolerance (5e-4) because the formula compounds four
            # constants (kB⁴, ℏ³, c²) — rounding in any one propagates.
            if rel > 5e-4:
                id3_pass = False
    tag = "PASS" if id3_pass else "FAIL"
    lines.append(f"  sigma formula  : {tag}" if id3_any else "  sigma formula  : SKIP")
    all_pass = all_pass and (id3_pass or not id3_any)

    return all_pass, lines


def clear():
    """Reset registry (for testing)."""
    _registry.clear()
