///|
pub(all) struct BottleneckAnalysisReport {
  takt_time_target_sec : Double
  primary_bottleneck_node_id : String
  primary_bottleneck_cycle_time : Double
  secondary_bottlenecks : Array[String]
  conveyor_bandwidth_bottlenecks : Array[String]
  max_theoretical_hourly_output : Double
  line_balance_efficiency_percent : Double
} derive(Debug, Eq)

///|
pub fn analyze_production_bottlenecks(
  graph : @graph.FactoryGraph,
) -> BottleneckAnalysisReport {
  let target_takt = graph.config().takt_time_target_sec
  let nodes = graph.get_all_nodes()

  let mut max_ct = 0.0
  let mut primary_id = ""
  let mut total_ct = 0.0
  let mut station_count = 0

  for node in nodes {
    let nt = node.node_type()
    if nt == @types.NodeType::Workstation ||
      nt == @types.NodeType::Machine ||
      nt == @types.NodeType::RobotArm ||
      nt == @types.NodeType::Inspection {
      let ct = node.cycle_time_sec()
      if ct > 0.0 {
        total_ct = total_ct + ct
        station_count = station_count + 1
        if ct > max_ct {
          max_ct = ct
          primary_id = node.id()
        }
      }
    }
  }

  let secondary = []
  for node in nodes {
    let nt = node.node_type()
    if nt == @types.NodeType::Workstation ||
      nt == @types.NodeType::Machine ||
      nt == @types.NodeType::RobotArm {
      let ct = node.cycle_time_sec()
      if ct > target_takt && node.id() != primary_id {
        secondary.push(node.id() + " (" + ct.to_string() + "s)")
      }
    }
  }

  // Conveyor bandwidth bottlenecks
  let edges = graph.get_all_edges()
  let conveyor_bottlenecks = []
  let required_th = if target_takt > 0.0 {
    3600.0 / target_takt
  } else {
    1200.0
  }
  for edge in edges {
    if edge.flow_type() == @types.FlowType::MaterialFlow {
      if edge.bandwidth() < required_th {
        conveyor_bottlenecks.push(
          edge.id() +
          " [" +
          edge.source_id() +
          " -> " +
          edge.target_id() +
          "]: " +
          edge.bandwidth().to_string() +
          " u/h",
        )
      }
    }
  }

  let max_throughput = if max_ct > 0.0 { 3600.0 / max_ct } else { 0.0 }
  let line_balance_eff = if station_count > 0 && max_ct > 0.0 {
    total_ct / (station_count.to_double() * max_ct) * 100.0
  } else {
    100.0
  }

  {
    takt_time_target_sec: target_takt,
    primary_bottleneck_node_id: primary_id,
    primary_bottleneck_cycle_time: max_ct,
    secondary_bottlenecks: secondary,
    conveyor_bandwidth_bottlenecks: conveyor_bottlenecks,
    max_theoretical_hourly_output: max_throughput,
    line_balance_efficiency_percent: line_balance_eff,
  }
}