"""Phaxor — Open Channel Flow Engine (Python port)"""
import math

def solve_open_channel(inputs: dict) -> dict | None:
    """Open Channel Flow Calculator."""
    shape = inputs.get('shape', 'trapezoidal')
    b = float(inputs.get('b', 0))
    y = float(inputs.get('y', 0))
    z = float(inputs.get('z', 0))
    s = float(inputs.get('S', 0))
    n = float(inputs.get('n', 0))

    if y <= 0 or s <= 0 or n <= 0:
        return None

    a_area = 0.0
    p_perim = 0.0
    t_top = 0.0

    if shape == 'rectangular':
        a_area = b * y
        p_perim = b + 2 * y
        t_top = b
    elif shape == 'trapezoidal':
        a_area = (b + z * y) * y
        p_perim = b + 2 * y * math.sqrt(1 + z * z)
        t_top = b + 2 * z * y
    elif shape == 'triangular':
        a_area = z * y * y
        p_perim = 2 * y * math.sqrt(1 + z * z)
        t_top = 2 * z * y
    elif shape == 'circular':
        d_diam = b
        if y > d_diam: return None
        r = d_diam / 2
        try:
            theta = 2 * math.acos(1 - y / r)
            a_area = (r * r / 2) * (theta - math.sin(theta))
            p_perim = r * theta
            t_top = 2 * math.sqrt(max(0, y * (d_diam - y)))
        except ValueError:
            return None
    elif shape == 'parabolic':
        t_top = b
        a_area = (2.0 / 3.0) * t_top * y
        p_perim = t_top + (8 * y * y) / (3 * t_top) if t_top > 0 else 0
    else:
        return None

    if a_area <= 0 or p_perim <= 0:
        return None

    hyd_r = a_area / p_perim
    velocity = (1.0 / n) * (hyd_r ** (2.0/3.0)) * (s ** 0.5)
    discharge = velocity * a_area

    dh = a_area / t_top if t_top > 0 else 0
    fr = velocity / math.sqrt(9.81 * dh) if dh > 0 else 0

    regime = 'Subcritical' if fr < 1 else 'Supercritical' if fr > 1 else 'Critical'

    # Critical Depth (Simplified verification)
    yc = 0.0
    if shape == 'rectangular' and b > 0:
        q_spec = discharge / b
        yc = (q_spec ** 2 / 9.81) ** (1.0/3.0)
    else:
        # Roughly approx based on hydraulic depth
        # No robust iterative solver included here to keep it simple/consistent with TS
        # Just return 0 or placeholder if not rect
        pass

    specific_e = y + (velocity ** 2) / (2 * 9.81)

    # qData generation skipped for Python engine unless needed for specific plotting output
    # Will stick to scalar results

    return {
        'A': float(f"{a_area:.4f}"),
        'P': float(f"{p_perim:.4f}"),
        'R': float(f"{hyd_r:.4f}"),
        'V': float(f"{velocity:.4f}"),
        'Q': float(f"{discharge:.4f}"),
        'Fr': float(f"{fr:.3f}"),
        'regime': regime,
        'yc': float(f"{yc:.3f}"),
        'E': float(f"{specific_e:.4f}"),
        'T': float(f"{t_top:.4f}"),
        'Dh': float(f"{dh:.4f}")
    }
