"""Phaxor — Beam Analysis Engine (Python port)"""


def solve_beam(inputs: dict) -> dict | None:
    """Solve a simply-supported beam for reactions, SFD, BMD, and deflection."""
    length = inputs.get('length', 0)
    E = inputs.get('E', 200)
    I_val = inputs.get('I', 1e6)
    supports = inputs.get('supports', [])
    point_loads = inputs.get('pointLoads', [])
    dist_loads = inputs.get('distributedLoads', [])
    moments = inputs.get('moments', [])

    if length <= 0 or len(supports) < 2:
        return None

    sorted_sups = sorted(supports, key=lambda s: s['position'])
    a = sorted_sups[0]['position']
    b = sorted_sups[1]['position']
    span = b - a
    if span <= 0:
        return None

    total_force = 0
    moment_about_a = 0

    for pl in point_loads:
        sign = -1 if pl.get('direction', 'down') == 'down' else 1
        F = pl['magnitude'] * sign
        total_force += F
        moment_about_a += F * (pl['position'] - a)

    for dl in dist_loads:
        w = (dl['startMag'] + dl['endMag']) / 2
        dl_len = dl['endPos'] - dl['startPos']
        F = -w * dl_len
        denom = 3 * (dl['startMag'] + dl['endMag'])
        centroid = dl['startPos'] + dl_len * (dl['startMag'] + 2 * dl['endMag']) / denom if denom else dl['startPos'] + dl_len / 2
        total_force += F
        moment_about_a += F * (centroid - a)

    net_moment = 0
    for m in moments:
        sign = -1 if m.get('direction', 'cw') == 'cw' else 1
        net_moment += m['magnitude'] * sign

    RB = -(moment_about_a + net_moment) / span
    RA = -total_force - RB
    reactions = [
        {'position': a, 'Fy': RA, 'Fx': 0, 'M': 0},
        {'position': b, 'Fy': RB, 'Fx': 0, 'M': 0},
    ]

    steps = 200
    dx = length / steps
    shear_data = []
    moment_data = []

    for i in range(steps + 1):
        x = i * dx
        V = 0
        M = 0

        for r in reactions:
            if x >= r['position']:
                V += r['Fy']
                M += r['Fy'] * (x - r['position'])

        for pl in point_loads:
            if x >= pl['position']:
                sign = -1 if pl.get('direction', 'down') == 'down' else 1
                V += pl['magnitude'] * sign
                M += pl['magnitude'] * sign * (x - pl['position'])

        for dl in dist_loads:
            if x > dl['startPos']:
                x_end = min(x, dl['endPos'])
                dl_len = x_end - dl['startPos']
                total_len = dl['endPos'] - dl['startPos']
                if total_len > 0:
                    w_start = dl['startMag']
                    w_end = w_start + (dl['endMag'] - w_start) * (dl_len / total_len)
                    w = (w_start + w_end) / 2
                    F = -w * dl_len
                    denom = 3 * (w_start + w_end)
                    centroid = dl['startPos'] + dl_len * (w_start + 2 * w_end) / denom if denom else dl['startPos'] + dl_len / 2
                    V += F
                    M += F * (x - centroid)

        for m in moments:
            if x >= m['position']:
                sign = -1 if m.get('direction', 'cw') == 'cw' else 1
                M += m['magnitude'] * sign

        shear_data.append({'x': round(x, 4), 'V': round(V, 4)})
        moment_data.append({'x': round(x, 4), 'M': round(M, 4)})

    max_shear = max(d['V'] for d in shear_data)
    min_shear = min(d['V'] for d in shear_data)
    max_moment = max(d['M'] for d in moment_data)
    min_moment = min(d['M'] for d in moment_data)

    return {
        'reactions': reactions,
        'shearData': shear_data,
        'momentData': moment_data,
        'maxShear': max_shear, 'minShear': min_shear,
        'maxMoment': max_moment, 'minMoment': min_moment,
    }
