from __future__ import annotations

from dataclasses import dataclass
import math

from .core import HydrologyError, _require_positive, _safe_round


@dataclass(frozen=True)
class WaveHeightInput:
    wind_speed_ms: float
    fetch_length_m: float
    average_depth_m: float
    gravity: float = 9.81
    height_factor: float = 1.0
    cumulative_frequency_percent: float | None = None
    wind_duration_h: float | None = None


@dataclass(frozen=True)
class WaveHeightResult:
    wind_speed_ms: float
    fetch_length_m: float
    average_depth_m: float
    dimensionless_depth: float
    dimensionless_fetch: float
    depth_term: float
    fetch_term: float
    mean_wave_height_m: float
    mean_wave_period_s: float
    wavelength_m: float
    design_wave_height_m: float
    height_factor: float
    height_factor_source: str
    cumulative_frequency_percent: float | None
    height_depth_ratio: float
    minimum_wind_duration_h: float
    wind_duration_h: float | None
    duration_sufficient: bool | None
    wave_steepness: float
    formula_note: str


@dataclass(frozen=True)
class WindSetupInput:
    base_water_level_m: float
    wind_speed_ms: float
    fetch_length_m: float
    average_depth_m: float
    wind_angle_deg: float = 0.0
    wind_stress_coefficient: float = 3.6e-6
    gravity: float = 9.81


@dataclass(frozen=True)
class WindSetupResult:
    base_water_level_m: float
    wind_speed_ms: float
    fetch_length_m: float
    average_depth_m: float
    wind_angle_deg: float
    direction_factor: float
    wind_stress_coefficient: float
    gravity: float
    wind_setup_m: float
    adjusted_water_level_m: float
    formula_note: str


def _require_finite(name: str, value: float) -> float:
    try:
        number = float(value)
    except (TypeError, ValueError) as exc:
        raise HydrologyError(f"{name} 必须是数字") from exc
    if not math.isfinite(number):
        raise HydrologyError(f"{name} 必须是有效数字")
    return number


def _wave_length_from_period(period_s: float, depth_m: float, gravity: float) -> float:
    deep_water_length = gravity * period_s * period_s / (2 * math.pi)
    length = deep_water_length
    for _ in range(80):
        next_length = deep_water_length * math.tanh(2 * math.pi * depth_m / max(length, 1e-9))
        if abs(next_length - length) < 1e-8:
            return next_length
        length = next_length
    return length


_WAVE_FREQUENCIES = (0.1, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 13.0, 20.0, 50.0)
_WAVE_HEIGHT_DEPTH_RATIOS = (0.0, 0.1, 0.2, 0.3, 0.4, 0.5)
_WAVE_HEIGHT_FACTORS = (
    (2.97, 2.42, 2.23, 2.11, 2.02, 1.95, 1.71, 1.61, 1.43, 0.94),
    (2.70, 2.26, 2.09, 2.00, 1.92, 1.86, 1.65, 1.56, 1.41, 0.96),
    (2.46, 2.09, 1.96, 1.88, 1.81, 1.76, 1.59, 1.51, 1.37, 0.98),
    (2.23, 1.93, 1.82, 1.76, 1.70, 1.66, 1.52, 1.45, 1.34, 1.00),
    (2.01, 1.78, 1.69, 1.64, 1.60, 1.56, 1.44, 1.39, 1.30, 1.01),
    (1.80, 1.63, 1.56, 1.52, 1.49, 1.46, 1.37, 1.33, 1.25, 1.01),
)


def _standard_wave_height_factor(frequency_percent: float, height_depth_ratio: float) -> float:
    try:
        column = _WAVE_FREQUENCIES.index(float(frequency_percent))
    except ValueError as exc:
        supported = "、".join(f"{item:g}%" for item in _WAVE_FREQUENCIES)
        raise HydrologyError(f"累积频率P仅支持规范表C.1.3中的{supported}") from exc
    if height_depth_ratio < 0 or height_depth_ratio > 0.5:
        raise HydrologyError(
            f"平均波高与水深比H/d={height_depth_ratio:.3f}超出规范表C.1.3的0~0.5范围，不能自动外推Kp"
        )
    for index in range(1, len(_WAVE_HEIGHT_DEPTH_RATIOS)):
        upper = _WAVE_HEIGHT_DEPTH_RATIOS[index]
        if height_depth_ratio <= upper:
            lower = _WAVE_HEIGHT_DEPTH_RATIOS[index - 1]
            ratio = (height_depth_ratio - lower) / (upper - lower)
            low_value = _WAVE_HEIGHT_FACTORS[index - 1][column]
            high_value = _WAVE_HEIGHT_FACTORS[index][column]
            return low_value + ratio * (high_value - low_value)
    return _WAVE_HEIGHT_FACTORS[-1][column]


def calculate_wave_height(data: WaveHeightInput) -> WaveHeightResult:
    wind = _require_positive("计算风速V", data.wind_speed_ms)
    fetch = _require_positive("风区长度F", data.fetch_length_m)
    depth = _require_positive("水域平均水深d", data.average_depth_m)
    gravity = _require_positive("重力加速度g", data.gravity)
    manual_factor = _require_positive("设计波高换算系数Kp", data.height_factor)

    dimensionless_depth = gravity * depth / (wind * wind)
    dimensionless_fetch = gravity * fetch / (wind * wind)
    depth_term = math.tanh(0.7 * dimensionless_depth ** 0.7)
    fetch_term = math.tanh(0.0018 * dimensionless_fetch ** 0.45 / max(depth_term, 1e-12))
    dimensionless_height = 0.13 * depth_term * fetch_term
    mean_height = dimensionless_height * wind * wind / gravity
    mean_period = 13.9 * wind / gravity * math.sqrt(max(dimensionless_height, 0.0))
    dimensionless_period = gravity * mean_period / wind
    minimum_duration = 168 * dimensionless_period ** 3.45 * wind / gravity
    minimum_duration_h = minimum_duration / 3600.0
    duration_h: float | None = None
    duration_sufficient: bool | None = None
    if data.wind_duration_h is not None:
        duration_h = _require_positive("实际持续风时", data.wind_duration_h)
        duration_sufficient = duration_h * 3600.0 >= minimum_duration
    height_depth_ratio = mean_height / depth
    frequency: float | None = None
    if data.cumulative_frequency_percent is None:
        factor = manual_factor
        factor_source = "手动输入"
    else:
        frequency = _require_positive("累积频率P", data.cumulative_frequency_percent)
        factor = _standard_wave_height_factor(frequency, height_depth_ratio)
        factor_source = "GB 50286-2013表C.1.3线性插值"
    wavelength = _wave_length_from_period(mean_period, depth, gravity)
    design_height = factor * mean_height
    steepness = design_height / wavelength if wavelength > 0 else 0.0

    return WaveHeightResult(
        wind_speed_ms=_safe_round(wind, 3),
        fetch_length_m=_safe_round(fetch, 3),
        average_depth_m=_safe_round(depth, 3),
        dimensionless_depth=_safe_round(dimensionless_depth, 6),
        dimensionless_fetch=_safe_round(dimensionless_fetch, 6),
        depth_term=_safe_round(depth_term, 6),
        fetch_term=_safe_round(fetch_term, 6),
        mean_wave_height_m=_safe_round(mean_height, 4),
        mean_wave_period_s=_safe_round(mean_period, 4),
        wavelength_m=_safe_round(wavelength, 4),
        design_wave_height_m=_safe_round(design_height, 4),
        height_factor=_safe_round(factor, 4),
        height_factor_source=factor_source,
        cumulative_frequency_percent=None if frequency is None else _safe_round(frequency, 3),
        height_depth_ratio=_safe_round(height_depth_ratio, 6),
        minimum_wind_duration_h=_safe_round(minimum_duration_h, 4),
        wind_duration_h=None if duration_h is None else _safe_round(duration_h, 4),
        duration_sufficient=duration_sufficient,
        wave_steepness=_safe_round(steepness, 6),
        formula_note=(
            "莆田试验站公式：gH/V^2=0.13*tanh[0.7*(gd/V^2)^0.7]*"
            "tanh{0.0018*(gF/V^2)^0.45/tanh[0.7*(gd/V^2)^0.7]}；"
            "gT/V=13.9*(gH/V^2)^0.5；gtmin/V=168*(gT/V)^3.45；"
            "设计波高换算可按表C.1.3；L按色散关系迭代。"
        ),
    )


def calculate_wind_setup(data: WindSetupInput) -> WindSetupResult:
    base_level = _require_finite("静水位Z0", data.base_water_level_m)
    wind = _require_positive("计算风速V", data.wind_speed_ms)
    fetch = _require_positive("风区长度D", data.fetch_length_m)
    depth = _require_positive("平均水深Hm", data.average_depth_m)
    angle = _require_finite("风向夹角β", data.wind_angle_deg)
    if angle < 0 or angle > 90:
        raise HydrologyError("风向夹角β应在0°~90°之间；0°为正向吹向计算岸段，90°为沿岸风")
    coefficient = _require_positive("风壅综合摩阻系数k", data.wind_stress_coefficient)
    gravity = _require_positive("重力加速度g", data.gravity)

    direction_factor = math.cos(math.radians(angle))
    setup = coefficient * wind * wind * fetch * direction_factor / (2 * gravity * depth)

    return WindSetupResult(
        base_water_level_m=_safe_round(base_level, 3),
        wind_speed_ms=_safe_round(wind, 3),
        fetch_length_m=_safe_round(fetch, 3),
        average_depth_m=_safe_round(depth, 3),
        wind_angle_deg=_safe_round(angle, 3),
        direction_factor=_safe_round(direction_factor, 6),
        wind_stress_coefficient=_safe_round(coefficient, 9),
        gravity=_safe_round(gravity, 3),
        wind_setup_m=_safe_round(setup, 4),
        adjusted_water_level_m=_safe_round(base_level + setup, 3),
        formula_note=(
            "风壅水面高度常用近似公式：hz = k*V^2*D*cosβ/(2*g*Hm)。"
            "式中V为计算风速，D为风区长度，Hm为平均水深，β为风向与计算岸段法线夹角，"
            "k为风壅综合摩阻系数，默认3.6E-6。"
        ),
    )
