from __future__ import annotations

from dataclasses import dataclass
import math


class WaterworksIrrigationError(ValueError):
    pass


def _positive(value: float, name: str) -> float:
    if value <= 0:
        raise WaterworksIrrigationError(f"{name}必须大于0")
    return value


def _non_negative(value: float, name: str) -> float:
    if value < 0:
        raise WaterworksIrrigationError(f"{name}不能小于0")
    return value


@dataclass(frozen=True)
class IrrigationModulusInput:
    irrigation_area_mu: float
    quota_m3_per_mu: float
    duration_days: float


@dataclass(frozen=True)
class IrrigationModulusResult:
    irrigation_modulus: float
    net_flow: float
    formula: str


def calculate_irrigation_modulus(data: IrrigationModulusInput) -> IrrigationModulusResult:
    area_mu = _positive(data.irrigation_area_mu, "灌溉面积")
    quota = _positive(data.quota_m3_per_mu, "灌水定额")
    days = _positive(data.duration_days, "灌水延续时间")
    modulus = quota / (8.64 * days)
    net_flow = modulus * area_mu / 10000.0
    return IrrigationModulusResult(
        irrigation_modulus=modulus,
        net_flow=net_flow,
        formula="q=m/(8.64*T)；Q净=q*A/10000",
    )


@dataclass(frozen=True)
class ContinuousDiversionInput:
    irrigation_area_10k_mu: float
    irrigation_modulus: float
    main_canal_efficiency: float
    branch_canal_efficiency: float
    lateral_canal_efficiency: float
    field_efficiency: float


@dataclass(frozen=True)
class ContinuousDiversionResult:
    canal_system_efficiency: float
    irrigation_efficiency: float
    net_flow: float
    gross_flow: float
    formula: str


def calculate_continuous_diversion(data: ContinuousDiversionInput) -> ContinuousDiversionResult:
    area = _positive(data.irrigation_area_10k_mu, "总控制灌溉面积")
    modulus = _positive(data.irrigation_modulus, "灌水模数")
    eta_main = _positive(data.main_canal_efficiency, "干渠水利用系数")
    eta_branch = _positive(data.branch_canal_efficiency, "支渠水利用系数")
    eta_lateral = _positive(data.lateral_canal_efficiency, "斗渠水利用系数")
    eta_field = _positive(data.field_efficiency, "田间水利用系数")
    for value, name in (
        (eta_main, "干渠水利用系数"),
        (eta_branch, "支渠水利用系数"),
        (eta_lateral, "斗渠水利用系数"),
        (eta_field, "田间水利用系数"),
    ):
        if value > 1:
            raise WaterworksIrrigationError(f"{name}不能大于1")
    canal_efficiency = eta_main * eta_branch * eta_lateral
    irrigation_efficiency = canal_efficiency * eta_field
    net_flow = area * modulus
    gross_flow = net_flow / irrigation_efficiency
    return ContinuousDiversionResult(
        canal_system_efficiency=canal_efficiency,
        irrigation_efficiency=irrigation_efficiency,
        net_flow=net_flow,
        gross_flow=gross_flow,
        formula="Q净=S*q；Q毛=Q净/η水；η水=η渠系*η田；η渠系=η干*η支*η斗",
    )


@dataclass(frozen=True)
class RotationalDiversionInput:
    group_count: int
    average_group_area_10k_mu: float
    irrigation_modulus: float
    irrigation_efficiency: float


@dataclass(frozen=True)
class RotationalDiversionResult:
    design_flow: float
    formula: str


def calculate_rotational_diversion(data: RotationalDiversionInput) -> RotationalDiversionResult:
    group_count = int(_positive(float(data.group_count), "轮灌组数"))
    area = _positive(data.average_group_area_10k_mu, "轮灌组平均灌溉面积")
    modulus = _positive(data.irrigation_modulus, "灌水模数")
    efficiency = _positive(data.irrigation_efficiency, "灌溉水利用系数")
    if efficiency > 1:
        raise WaterworksIrrigationError("灌溉水利用系数不能大于1")
    return RotationalDiversionResult(
        design_flow=group_count * modulus * area / efficiency,
        formula="Qn=N*q*An/ηn",
    )


@dataclass(frozen=True)
class CanalLossSegment:
    slope: float
    length_m: float


@dataclass(frozen=True)
class CanalHeadwaterInput:
    field_elevation: float
    field_water_depth: float
    channel_water_depth: float
    structure_head_loss: float
    canal_segments: tuple[CanalLossSegment, ...] = ()
    extra_canal_head_loss: float = 0.0


@dataclass(frozen=True)
class CanalHeadwaterResult:
    canal_head_loss: float
    headwater_elevation: float
    formula: str


def calculate_canal_headwater(data: CanalHeadwaterInput) -> CanalHeadwaterResult:
    field_elevation = float(data.field_elevation)
    field_depth = _non_negative(data.field_water_depth, "田面灌水深度")
    channel_depth = _non_negative(data.channel_water_depth, "渠道设计水深")
    structure_loss = _non_negative(data.structure_head_loss, "建筑物水头损失")
    extra_loss = _non_negative(data.extra_canal_head_loss, "直接填写渠道水头损失")
    segment_loss = 0.0
    for segment in data.canal_segments:
        slope = _non_negative(segment.slope, "渠道比降")
        length = _non_negative(segment.length_m, "渠道累计长度")
        segment_loss += slope * length
    canal_loss = segment_loss + extra_loss
    headwater = field_elevation + field_depth + canal_loss + structure_loss + channel_depth
    return CanalHeadwaterResult(
        canal_head_loss=canal_loss,
        headwater_elevation=headwater,
        formula="H首=h田+∇h灌+∇h渠+∇h建+h水；∇h渠=Σ(i*L)+直接填写损失",
    )


@dataclass(frozen=True)
class PumpTypicalYearFlowInput:
    max_gross_irrigation_depth_mm: float
    irrigation_area_hm2: float
    pumping_time_seconds: float


@dataclass(frozen=True)
class PumpDesignRateFlowInput:
    crop_ratio_percent: float
    quota_m3_per_mu: float
    duration_days: float
    daily_pumping_hours: float
    total_area_10k_mu: float
    irrigation_efficiency: float


@dataclass(frozen=True)
class PumpDesignFlowResult:
    design_flow: float
    irrigation_rate: float
    formula: str


def calculate_pump_flow_by_typical_year(data: PumpTypicalYearFlowInput) -> PumpDesignFlowResult:
    depth = _positive(data.max_gross_irrigation_depth_mm, "典型年最大毛灌溉用水量")
    area = _positive(data.irrigation_area_hm2, "灌溉面积")
    seconds = _positive(data.pumping_time_seconds, "时段内提水时间")
    flow = 10.0 * depth * area / seconds
    return PumpDesignFlowResult(
        design_flow=flow,
        irrigation_rate=0.0,
        formula="Q=10*M*ω/t",
    )


def calculate_pump_flow_by_design_rate(data: PumpDesignRateFlowInput) -> PumpDesignFlowResult:
    ratio = _positive(data.crop_ratio_percent, "作物种植比例")
    quota = _positive(data.quota_m3_per_mu, "灌水定额")
    duration = _positive(data.duration_days, "灌水延续时间")
    hours = _positive(data.daily_pumping_hours, "每天灌水时间")
    area = _positive(data.total_area_10k_mu, "总灌溉面积")
    efficiency = _positive(data.irrigation_efficiency, "灌溉水利用系数")
    if efficiency > 1:
        raise WaterworksIrrigationError("灌溉水利用系数不能大于1")
    irrigation_rate = (ratio / 100.0) * quota / (0.36 * duration * hours)
    flow = irrigation_rate * area / efficiency
    return PumpDesignFlowResult(
        design_flow=flow,
        irrigation_rate=irrigation_rate,
        formula="q净=a*m/(0.36*T*t)；Q=q净*A/η灌",
    )


@dataclass(frozen=True)
class PressurePipeInput:
    design_flow: float
    allowable_velocity: float


@dataclass(frozen=True)
class PressurePipeResult:
    inner_diameter_m: float
    minimum_wall_thickness_mm: float
    vent_area_m2: float
    formula: str


def calculate_pressure_pipe(data: PressurePipeInput) -> PressurePipeResult:
    flow = _positive(data.design_flow, "管道设计流量")
    velocity = _positive(data.allowable_velocity, "管道允许流速")
    diameter = math.sqrt(4.0 * flow / (math.pi * velocity))
    thickness = diameter * 1000.0 / 800.0 + 4.0
    vent_area = 0.01 * flow
    return PressurePipeResult(
        inner_diameter_m=diameter,
        minimum_wall_thickness_mm=thickness,
        vent_area_m2=vent_area,
        formula="d=(4Q/(πv))^0.5；δ=d(mm)/800+4；S≥0.01Q",
    )


@dataclass(frozen=True)
class PumpHeadInput:
    outlet_water_level: float
    suction_water_level: float
    total_head_loss: float


@dataclass(frozen=True)
class PumpHeadResult:
    static_head: float
    design_head: float
    formula: str


def calculate_pump_design_head(data: PumpHeadInput) -> PumpHeadResult:
    loss = _non_negative(data.total_head_loss, "总水头损失")
    static_head = float(data.outlet_water_level) - float(data.suction_water_level)
    design_head = static_head + loss
    return PumpHeadResult(
        static_head=static_head,
        design_head=design_head,
        formula="Hp=H净+ΔH；H净=Z1-Z2",
    )


@dataclass(frozen=True)
class PumpInstallationInput:
    allowable_suction_vacuum_m: float
    atmospheric_pressure_pa: float
    vapor_pressure_head_m: float
    water_density_kg_m3: float
    suction_velocity_m_s: float
    suction_pipe_head_loss_m: float
    gravity: float = 9.81


@dataclass(frozen=True)
class PumpInstallationResult:
    allowable_installation_height_m: float
    formula: str


def calculate_pump_installation_height(data: PumpInstallationInput) -> PumpInstallationResult:
    hs = float(data.allowable_suction_vacuum_m)
    pa = _positive(data.atmospheric_pressure_pa, "当地大气压")
    pv_head = _non_negative(data.vapor_pressure_head_m, "汽化压头")
    rho = _positive(data.water_density_kg_m3, "水密度")
    velocity = _non_negative(data.suction_velocity_m_s, "水泵入口平均流速")
    head_loss = _non_negative(data.suction_pipe_head_loss_m, "吸水管路水力损失")
    g = _positive(data.gravity, "重力加速度")
    height = hs - 10.33 + pa / (rho * g) + 0.24 - pv_head - velocity * velocity / (2.0 * g) - head_loss
    return PumpInstallationResult(
        allowable_installation_height_m=height,
        formula="[Hg]=[Hs]-10.33+pa/(ρg)+0.24-pv/(ρg)-v²/(2g)-hw",
    )


@dataclass(frozen=True)
class PumpPowerInput:
    density_t_m3: float
    irrigation_modulus_m3_s_hm2: float
    guaranteed_irrigation_area_hm2: float
    design_head_m: float
    pump_efficiency_percent: float


@dataclass(frozen=True)
class PumpEfficiencyInput:
    density_kg_m3: float
    design_flow_m3_s: float
    design_head_m: float
    shaft_power_kw: float
    gravity: float = 9.81


@dataclass(frozen=True)
class PumpSpecificSpeedInput:
    rated_speed_rpm: float
    rated_flow_m3_s: float
    rated_head_m: float
    double_suction: bool = False


@dataclass(frozen=True)
class PumpPerformanceResult:
    total_power_kw: float = 0.0
    effective_power_kw: float = 0.0
    efficiency_percent: float = 0.0
    specific_speed: float = 0.0
    formula: str = ""


def calculate_pump_total_power(data: PumpPowerInput) -> PumpPerformanceResult:
    density = _positive(data.density_t_m3, "水的密度")
    modulus = _positive(data.irrigation_modulus_m3_s_hm2, "设计毛灌溉模数")
    area = _positive(data.guaranteed_irrigation_area_hm2, "保证灌溉面积")
    head = _positive(data.design_head_m, "水泵设计扬程")
    efficiency = _positive(data.pump_efficiency_percent, "水泵装置效率") / 100.0
    power = density * 1000.0 * modulus * area * head / (102.0 * efficiency)
    return PumpPerformanceResult(
        total_power_kw=power,
        formula="N=ρ*q*ω*Hp/(102η)，ρ按t/m³输入、η按百分数输入",
    )


def calculate_pump_efficiency(data: PumpEfficiencyInput) -> PumpPerformanceResult:
    rho = _positive(data.density_kg_m3, "水密度")
    flow = _positive(data.design_flow_m3_s, "水泵流量")
    head = _positive(data.design_head_m, "水泵设计扬程")
    shaft_power = _positive(data.shaft_power_kw, "水泵轴功率")
    g = _positive(data.gravity, "重力加速度")
    effective = rho * g * flow * head / 1000.0
    efficiency = effective / shaft_power * 100.0
    return PumpPerformanceResult(
        effective_power_kw=effective,
        efficiency_percent=efficiency,
        formula="η=Pe/P；Pe=ρgQHp/1000",
    )


def calculate_pump_specific_speed(data: PumpSpecificSpeedInput) -> PumpPerformanceResult:
    speed = _positive(data.rated_speed_rpm, "额定转速")
    flow = _positive(data.rated_flow_m3_s, "额定流量")
    head = _positive(data.rated_head_m, "额定扬程")
    if data.double_suction:
        flow = flow / 2.0
    ns = 3.65 * speed * math.sqrt(flow) / (head ** 0.75)
    return PumpPerformanceResult(
        specific_speed=ns,
        formula="ns=3.65*n*sqrt(Q)/H^(3/4)，双吸泵Q取Q/2",
    )


@dataclass(frozen=True)
class TransformerMotorInput:
    count: int
    motor_rated_power_kw: float
    pump_shaft_power_kw: float
    motor_efficiency_percent: float
    power_factor: float
    correction_factor: float


@dataclass(frozen=True)
class TransformerCapacityInput:
    motors: tuple[TransformerMotorInput, ...]
    lighting_load_kw: float
    lighting_coincidence_factor: float


@dataclass(frozen=True)
class TransformerCapacityResult:
    motor_load_kva: float
    lighting_load_kva: float
    total_capacity_kva: float
    formula: str


def calculate_transformer_capacity(data: TransformerCapacityInput) -> TransformerCapacityResult:
    motor_total = 0.0
    for motor in data.motors:
        count_value = _non_negative(float(motor.count), "电动机台数")
        if not count_value.is_integer():
            raise WaterworksIrrigationError("电动机台数必须为整数")
        count = int(count_value)
        if count == 0:
            continue
        rated_power = _positive(motor.motor_rated_power_kw, "电动机额定功率")
        shaft_power = _positive(motor.pump_shaft_power_kw, "水泵轴功率")
        efficiency = _positive(motor.motor_efficiency_percent, "电动机效率")
        if not 1 < efficiency <= 100:
            raise WaterworksIrrigationError("电动机效率按百分数填写，例如80代表80%，不要填0.8；范围为(1,100]")
        efficiency /= 100.0
        power_factor = _positive(motor.power_factor, "电动机功率因数")
        if power_factor > 1:
            raise WaterworksIrrigationError("电动机功率因数不能大于1")
        correction = _positive(motor.correction_factor, "修正系数")
        load_factor = correction * shaft_power / rated_power
        motor_total += count * rated_power / efficiency / power_factor * load_factor
    lighting = _non_negative(data.lighting_load_kw, "照明等用电总负荷") * _positive(
        data.lighting_coincidence_factor, "照明同时系数"
    )
    return TransformerCapacityResult(
        motor_load_kva=motor_total,
        lighting_load_kva=lighting,
        total_capacity_kva=motor_total + lighting,
        formula="S=Σ[N×P1/(η%/100)/cosφ×k1]+P2×k2；k1=k3×P3/P1。η输入80代表0.80；仅为运行负荷估算，选型另核启动冲击、裕度与谐波。",
    )
