from __future__ import annotations

from dataclasses import dataclass
import math

from .core import HydrologyError, _require_positive, _safe_round


BACKWATER_ETA_ROWS = (
    ("河滩很小的山区河流", 10.0, 0.05),
    ("河滩较小的半山区河流", 30.0, 0.07),
    ("有中等河滩的平原河流", 50.0, 0.10),
    ("河滩较大的低洼区河流", float("inf"), 0.15),
)

BACKWATER_SOIL_TYPES = (
    "松软土",
    "中等土",
    "密实土",
)


@dataclass(frozen=True)
class BackwaterInput:
    design_flow_m3s: float
    full_section_area_m2: float
    natural_channel_velocity_ms: float
    bridge_actual_area_m2: float
    flow_angle_deg: float
    soil_type: str
    obstruction_area_ratio_percent: float
    natural_water_surface_slope: float
    blocked_flow_ratio_percent: float | None = None
    eta_manual: float | None = None
    eta_source_label: str = ""


@dataclass(frozen=True)
class BackwaterResult:
    design_flow_m3s: float
    eta: float
    eta_source: str
    average_velocity_v_ms: float
    natural_channel_velocity_vp_ms: float
    design_bridge_area_wx_m2: float
    actual_bridge_area_wg_m2: float
    obstruction_area_m2: float
    obstruction_area_ratio_percent: float
    blocked_flow_ratio_percent: float
    scour_coefficient_p: float
    bridge_velocity_vm_ms: float
    max_backwater_height_m: float
    backwater_curve_length_m: float
    soil_type: str
    formula_note: str


def eta_from_blocked_flow_ratio(blocked_flow_ratio_percent: float) -> tuple[float, str]:
    ratio = max(0.0, float(blocked_flow_ratio_percent))
    for river_type, upper, eta in BACKWATER_ETA_ROWS:
        if ratio <= upper:
            return eta, f"{river_type}，河滩路堤阻挡流量比{ratio:g}%"
    return 0.15, f"河滩较大的低洼区河流，河滩路堤阻挡流量比{ratio:g}%"


def eta_from_obstruction_area_ratio(obstruction_area_ratio_percent: float) -> tuple[float, str]:
    """Compatibility alias; the percentage is a blocked-flow ratio, not an area ratio."""
    return eta_from_blocked_flow_ratio(obstruction_area_ratio_percent)


def obstruction_area_ratio_percent(full_section_area_m2: float, effective_area_m2: float) -> float:
    full_area = _require_positive("全河天然过水断面面积ω", full_section_area_m2)
    effective_area = _require_positive("建后有效过水断面面积ωg", effective_area_m2)
    obstruction_area = max(0.0, full_area - effective_area)
    return obstruction_area / full_area * 100


def bridge_velocity_by_soil(soil_type: str, vp: float, p_value: float) -> float:
    soil = soil_type.strip()
    if soil == "松软土":
        return vp
    if soil == "中等土":
        return vp * 2 * p_value / (p_value + 1)
    if soil == "密实土":
        return p_value * vp
    raise HydrologyError(f"不支持的土质类型：{soil_type}")


def calculate_backwater(data: BackwaterInput) -> BackwaterResult:
    q = _require_positive("设计流量QP", data.design_flow_m3s)
    full_area = _require_positive("全河过水断面面积", data.full_section_area_m2)
    vp = _require_positive("设计流速VP", data.natural_channel_velocity_ms)
    wg = _require_positive("建后有效过水断面面积ωg", data.bridge_actual_area_m2)
    slope = _require_positive("天然水面坡度I0", data.natural_water_surface_slope)
    alpha = math.radians(abs(float(data.flow_angle_deg)))
    cos_alpha = math.cos(alpha)
    if cos_alpha <= 1e-9:
        raise HydrologyError("水流交角alpha不能接近90°，否则cos(alpha)无法用于A.2.3-3计算")

    blocked_flow_ratio = (
        data.blocked_flow_ratio_percent
        if data.blocked_flow_ratio_percent is not None
        else data.obstruction_area_ratio_percent
    )
    if not math.isfinite(float(blocked_flow_ratio)) or float(blocked_flow_ratio) < 0:
        raise HydrologyError("河滩路堤阻挡流量比必须是大于或等于0的百分数")
    if data.eta_manual is not None and data.eta_manual > 0:
        eta = float(data.eta_manual)
        eta_source = data.eta_source_label.strip() or "手动输入"
    else:
        eta, eta_source = eta_from_blocked_flow_ratio(blocked_flow_ratio)

    v = q / full_area
    wx = q / (vp * cos_alpha)
    obstruction_area = max(0.0, full_area - wg)
    obstruction_ratio = obstruction_area / full_area * 100
    p_value = wx / wg
    vm = bridge_velocity_by_soil(data.soil_type, vp, p_value)
    delta_z = max(0.0, eta * (vm * vm - v * v))
    length = 2 * delta_z / slope

    return BackwaterResult(
        design_flow_m3s=_safe_round(q, 3),
        eta=_safe_round(eta, 4),
        eta_source=eta_source,
        average_velocity_v_ms=_safe_round(v, 4),
        natural_channel_velocity_vp_ms=_safe_round(vp, 4),
        design_bridge_area_wx_m2=_safe_round(wx, 4),
        actual_bridge_area_wg_m2=_safe_round(wg, 4),
        obstruction_area_m2=_safe_round(obstruction_area, 4),
        obstruction_area_ratio_percent=_safe_round(obstruction_ratio, 4),
        blocked_flow_ratio_percent=_safe_round(blocked_flow_ratio, 4),
        scour_coefficient_p=_safe_round(p_value, 4),
        bridge_velocity_vm_ms=_safe_round(vm, 4),
        max_backwater_height_m=_safe_round(delta_z, 4),
        backwater_curve_length_m=_safe_round(length, 2),
        soil_type=data.soil_type.strip(),
        formula_note=(
            "ΔZₘ=η(Vₘ²-V²)；P=ωx/ωg；ωx=Qₚ/(Vₚ·cosα)；"
            "Lᵧ=2ΔZₘ/I₀。Vₘ按土质由表A.2.3-2计算。"
        ),
    )
