"""
Poynting theorem (∂u/∂t + ∇·S + J·E = 0).

Assertion-based CAS audit block.
Pillar: Electromagnetism | Chain: u definition → ∂u/∂t → Maxwell substitution → vector identity → S → theorem
CalRef: Math Appendix §4.2–4.3, EM Calibration §4B
"""


def run():
    from sympy import symbols, Function, diff, simplify

    print('=== CAS AUDIT: F0025 — Poynting theorem ===\n')

    pass_count = 0
    fail_count = 0
    total_steps = 0

    x, y, z, t = symbols('x y z t', real=True)
    mu0, eps0 = symbols('mu0 eps0', real=True, positive=True)

    # E, B, J fields
    Ex = Function('Ex')(x, y, z, t)
    Ey = Function('Ey')(x, y, z, t)
    Ez = Function('Ez')(x, y, z, t)
    Bx = Function('Bx')(x, y, z, t)
    By = Function('By')(x, y, z, t)
    Bz = Function('Bz')(x, y, z, t)
    Jx = Function('Jx')(x, y, z, t)
    Jy = Function('Jy')(x, y, z, t)
    Jz = Function('Jz')(x, y, z, t)

    # Energy density
    E_sq = Ex**2 + Ey**2 + Ez**2
    B_sq = Bx**2 + By**2 + Bz**2
    u = (eps0 * E_sq + B_sq / mu0) / 2

    # Poynting vector
    Sx = (Ey*Bz - Ez*By) / mu0
    Sy = (Ez*Bx - Ex*Bz) / mu0
    Sz = (Ex*By - Ey*Bx) / mu0

    # Ohmic dissipation
    JdotE = Jx*Ex + Jy*Ey + Jz*Ez

    print('Section D: Step log')
    print('---------------------------------------------')

    # --- Step 1: ∂u/∂t chain rule ---
    du_dt = diff(u, t)
    du_dt_expected = eps0*(Ex*diff(Ex,t) + Ey*diff(Ey,t) + Ez*diff(Ez,t)) + (Bx*diff(Bx,t) + By*diff(By,t) + Bz*diff(Bz,t))/mu0
    res1 = simplify(du_dt - du_dt_expected)
    total_steps += 1
    if simplify(res1) == 0:
        print('  Step 1  PASS — ∂u/∂t = ε₀(E·∂E/∂t) + (1/μ₀)(B·∂B/∂t)')
        pass_count += 1
    else:
        print('  Step 1  FAIL')
        fail_count += 1

    # --- Step 2-3: Maxwell substitutions are verified structurally ---
    # Faraday: ∂B/∂t = −∇×E
    dBx_dt_faraday = -(diff(Ez, y) - diff(Ey, z))
    dBy_dt_faraday = -(diff(Ex, z) - diff(Ez, x))
    dBz_dt_faraday = -(diff(Ey, x) - diff(Ex, y))

    BdotdBdt_faraday = Bx*dBx_dt_faraday + By*dBy_dt_faraday + Bz*dBz_dt_faraday

    # Ampère–Maxwell: ∂E/∂t = (1/(μ₀ε₀))(∇×B − μ₀J)
    curlB_x = diff(Bz,y) - diff(By,z)
    curlB_y = diff(Bx,z) - diff(Bz,x)
    curlB_z = diff(By,x) - diff(Bx,y)

    dEx_dt_ampere = (curlB_x - mu0*Jx) / (mu0*eps0)
    dEy_dt_ampere = (curlB_y - mu0*Jy) / (mu0*eps0)
    dEz_dt_ampere = (curlB_z - mu0*Jz) / (mu0*eps0)

    EdotdEdt_ampere = Ex*dEx_dt_ampere + Ey*dEy_dt_ampere + Ez*dEz_dt_ampere

    # --- Step 4: du/dt after Maxwell substitution ---
    du_dt_maxwell = eps0*EdotdEdt_ampere + BdotdBdt_faraday/mu0

    curlE_x = diff(Ez,y) - diff(Ey,z)
    curlE_y = diff(Ex,z) - diff(Ez,x)
    curlE_z = diff(Ey,x) - diff(Ex,y)

    EdotcurlB = Ex*curlB_x + Ey*curlB_y + Ez*curlB_z
    BdotcurlE = Bx*curlE_x + By*curlE_y + Bz*curlE_z

    du_dt_step23 = (EdotcurlB - BdotcurlE)/mu0 - JdotE
    res4 = simplify(du_dt_maxwell - du_dt_step23)
    total_steps += 1
    if simplify(res4) == 0:
        print('  Step 4  PASS — ∂u/∂t = (1/μ₀)[E·(∇×B)−B·(∇×E)] − J·E')
        pass_count += 1
    else:
        print('  Step 4  FAIL')
        fail_count += 1

    # --- Step 5: Vector identity ---
    divExB = diff(Ey*Bz - Ez*By, x) + diff(Ez*Bx - Ex*Bz, y) + diff(Ex*By - Ey*Bx, z)
    identity_rhs = BdotcurlE - EdotcurlB
    res5 = simplify(divExB - identity_rhs)
    total_steps += 1
    if simplify(res5) == 0:
        print('  Step 5  PASS — ∇·(E×B) = B·(∇×E) − E·(∇×B) (vector identity)')
        pass_count += 1
    else:
        print('  Step 5  FAIL')
        fail_count += 1

    # --- Step 6: ∇·S from vector identity ---
    divS = diff(Sx, x) + diff(Sy, y) + diff(Sz, z)
    divS_expected = divExB / mu0
    res6 = simplify(divS - divS_expected)
    total_steps += 1
    if simplify(res6) == 0:
        print('  Step 6  PASS — ∇·S = (1/μ₀)∇·(E×B)')
        pass_count += 1
    else:
        print('  Step 6  FAIL')
        fail_count += 1

    # --- Step 7: Poynting theorem assembly ---
    poynting_sum = du_dt_maxwell + divS + JdotE
    poynting_sum_simplified = simplify(poynting_sum)
    total_steps += 1
    if simplify(poynting_sum_simplified) == 0:
        print('  Step 7  PASS — ∂u/∂t + ∇·S + J·E = 0 (Poynting theorem)')
        pass_count += 1
    else:
        print('  Step 7  FAIL')
        fail_count += 1

    # --- Step 8: Source-free limit ---
    poynting_J0 = poynting_sum.subs([(Jx, 0), (Jy, 0), (Jz, 0)])
    res8 = simplify(poynting_J0)
    total_steps += 1
    if simplify(res8) == 0:
        print('  Step 8  PASS — J=0 → ∂u/∂t + ∇·S = 0 (consistent with F0015)')
        pass_count += 1
    else:
        print('  Step 8  FAIL')
        fail_count += 1

    # --- Step 9: Numerical — resistive dissipation ---
    sigma_Cu = 5.96e7
    E_val = 0.01
    JE_val = sigma_Cu * E_val**2
    JE_expected = 5960
    total_steps += 1
    if abs(JE_val - JE_expected) < 1:
        print(f'  Step 9  PASS — Cu dissipation: J·E = {JE_val:.0f} W/m³ (σ=5.96e7, E=0.01 V/m)')
        pass_count += 1
    else:
        print('  Step 9  FAIL')
        fail_count += 1

    # --- Step 10: Self-test — wrong Faraday sign ---
    dBx_wrong = (diff(Ez, y) - diff(Ey, z))
    dBy_wrong = (diff(Ex, z) - diff(Ez, x))
    dBz_wrong = (diff(Ey, x) - diff(Ex, y))

    BdotdBdt_wrong = Bx*dBx_wrong + By*dBy_wrong + Bz*dBz_wrong
    du_dt_wrong = eps0*EdotdEdt_ampere + BdotdBdt_wrong/mu0
    poynting_wrong = du_dt_wrong + divS + JdotE
    poynting_wrong_s = simplify(poynting_wrong)
    total_steps += 1
    if not (simplify(poynting_wrong_s) == 0):
        print('  Step 10 PASS — Wrong Faraday sign (+∇×E) breaks Poynting theorem')
        pass_count += 1
    else:
        print('  Step 10 FAIL')
        fail_count += 1

    # --- Step 11: Self-test — missing J·E term ---
    poynting_noJ = du_dt_maxwell + divS
    res11_check = simplify(poynting_noJ + JdotE)
    total_steps += 1
    if simplify(res11_check) == 0:
        print('  Step 11 PASS — Missing J·E: residual = −J·E (quantified)')
        pass_count += 1
    else:
        print('  Step 11 FAIL')
        fail_count += 1

    print('---------------------------------------------\n')

    # ---- VERDICT ----
    print('=============================================')
    print('  F0025 AUDIT RESULT')
    print(f'  Steps: {total_steps}  |  Pass: {pass_count}  |  Fail: {fail_count}')
    if fail_count == 0:
        print('  STATUS: *** PASS ***')
    else:
        print(f'  STATUS: *** FAIL *** ({fail_count} step(s) failed)')
    print('=============================================')
    print(f'  ✓ F0025 — {pass_count}/{total_steps} PASS')


if __name__ == '__main__':
    run()
