///|
fn print_banner() -> Unit {
  println(
    "================================================================================",
  )
  println(
    "                MoonBit FactoryGraph - Industrial Plant Engine                  ",
  )
  println(
    "       Multi-Layer Topology, Material/Control/Energy Flow & Failure Twin        ",
  )
  println(
    "================================================================================",
  )
}

///|
fn print_help() -> Unit {
  print_banner()
  println("Usage: moon run cmd/main -- [COMMAND] [OPTIONS]")
  println("")
  println("Commands:")
  println(
    "  info                     Display plant summary, assets & power grid",
  )
  println(
    "  health                   Inspect topology completeness and flow layers",
  )
  println(
    "  capacity                 Analyze material demand and edge utilization",
  )
  println(
    "  readiness                Audit deployment readiness and release blockers",
  )
  println(
    "  analyze                  Run bottleneck, SPOF and resilience analysis",
  )
  println(
    "  outage          Simulate device outage and failure cascade",
  )
  println(
    "  path           Find optimal material flow routing",
  )
  println(
    "  diff           Compare two topologies and compute risk",
  )
  println(
    "  export           Export plant model (dot|mermaid|puml|json|report|csv)",
  )
  println(
    "  optimize                 Generate line balancing & energy schedules",
  )
  println(
    "  demo                             Run full end-to-end multi-factory demo",
  )
  println(
    "  bench                            Run high-scale graph scaling benchmark",
  )
  println("")
  println("Available Presets:")
  println("  automotive, battery, semiconductor, chemical")
}

///|
fn get_preset_graph(name : String) -> @graph.FactoryGraph? {
  match name {
    "automotive" | "auto" => Some(@lib.load_automotive_plant())
    "battery" | "bat" => Some(@lib.load_battery_plant())
    "semiconductor" | "semi" => Some(@lib.load_semiconductor_plant())
    "chemical" | "chem" => Some(@lib.load_chemical_plant())
    _ => None
  }
}

///|
fn cmd_info(preset_name : String) -> Unit {
  match get_preset_graph(preset_name) {
    None =>
      println(
        "Error: Unknown preset '" +
        preset_name +
        "'. Available: automotive, battery, semiconductor, chemical",
      )
    Some(g) => {
      let cfg = g.config()
      let kpis = @analysis.compute_plant_kpis(g)
      let zones = g.get_all_zones()
      let szones = g.get_all_safety_zones()

      println("Plant Topology Information:")
      println("  Facility Name    : " + cfg.plant_name)
      println("  Location         : " + cfg.site_location)
      println("  Model Version    : " + cfg.version)
      println(
        "  Target Takt Time : " + cfg.takt_time_target_sec.to_string() + "s",
      )
      println(
        "  Total Assets     : " +
        kpis.total_asset_count.to_string() +
        " (" +
        kpis.active_asset_count.to_string() +
        " active)",
      )
      println("  Total Edges      : " + g.edge_count().to_string())
      println(
        "  Total Connected Power: " +
        @types.format_double(kpis.total_power_demand_kw, 1) +
        " kW",
      )
      println("  Workshop Zones   : " + zones.length().to_string())
      for z in zones {
        let zn = g.get_nodes_in_zone(z)
        println("    - " + z + ": " + zn.length().to_string() + " nodes")
      }
      println("  Safety Domains   : " + szones.length().to_string())
    }
  }
}

///|
fn cmd_health(preset_name : String) -> Unit {
  match get_preset_graph(preset_name) {
    None => println("Error: Unknown preset '" + preset_name + "'")
    Some(g) => {
      let report = @graph.inspect_graph_health(g)
      println("================ Topology Health ================")
      println(
        "Assets: " +
        report.active_node_count.to_string() +
        "/" +
        report.total_node_count.to_string() +
        " active",
      )
      println(
        "Edges: " +
        report.active_edge_count.to_string() +
        "/" +
        report.total_edge_count.to_string() +
        " active",
      )
      println("Orphan nodes: " + report.orphan_node_ids.length().to_string())
      for layer in report.layer_health {
        if layer.active_edge_count > 0 || layer.inactive_edge_count > 0 {
          println(
            "  " +
            layer.flow_type.to_string() +
            ": " +
            layer.active_edge_count.to_string() +
            " active, " +
            @types.format_double(layer.total_bandwidth_units_per_hour, 1) +
            " units/h capacity",
          )
        }
      }
      for node_id in report.orphan_node_ids {
        println("  Orphan: " + node_id)
      }
    }
  }
}

///|
fn cmd_capacity(preset_name : String) -> Unit {
  match get_preset_graph(preset_name) {
    None => println("Error: Unknown preset '" + preset_name + "'")
    Some(g) => {
      let report = @analysis.analyze_material_capacity(g)
      println("================ Material Capacity ================")
      println(
        "Demand: " +
        @types.format_double(report.demand_per_hour, 1) +
        " units/hour",
      )
      println(
        "Output capacity: " +
        @types.format_double(report.output_capacity_per_hour, 1) +
        " units/hour",
      )
      println(
        "Capacity margin: " +
        @types.format_double(report.capacity_margin_percent, 1) +
        "%",
      )
      println(
        "Overloaded edges: " + report.overloaded_edge_ids.length().to_string(),
      )
      for edge_id in report.overloaded_edge_ids {
        println("  Congested: " + edge_id)
      }
      println(
        "Constrained assets: " +
        report.constrained_node_ids.length().to_string(),
      )
      for node_id in report.constrained_node_ids {
        println("  Constrained: " + node_id)
      }
    }
  }
}

///|
fn cmd_readiness(preset_name : String) -> Unit {
  match get_preset_graph(preset_name) {
    None => println("Error: Unknown preset '" + preset_name + "'")
    Some(g) => {
      let report = @analysis.audit_operational_readiness(g)
      println("================ Operational Readiness ================")
      println("Level: " + report.readiness_level.to_string())
      println(
        "Ready for deployment: " + report.ready_for_deployment.to_string(),
      )
      println(
        "Readiness score: " +
        @types.format_double(report.readiness_score, 1) +
        " / 100",
      )
      println(
        "Validation issues: " +
        report.total_validation_issue_count.to_string() +
        " (" +
        report.critical_issue_count.to_string() +
        " critical, " +
        report.error_issue_count.to_string() +
        " error, " +
        report.warning_issue_count.to_string() +
        " warning)",
      )
      println(
        "Active topology: " +
        @types.format_double(report.active_node_ratio_percent, 1) +
        "% nodes, " +
        @types.format_double(report.active_edge_ratio_percent, 1) +
        "% edges",
      )
      println(
        "Mean asset availability: " +
        @types.format_double(report.mean_asset_availability_percent, 2) +
        "%",
      )
      println(
        "Capacity margin: " +
        @types.format_double(report.capacity_margin_percent, 1) +
        "%",
      )
      for recommendation in report.recommendations {
        println("  - " + recommendation)
      }
    }
  }
}

///|
fn cmd_analyze(preset_name : String) -> Unit {
  match get_preset_graph(preset_name) {
    None => println("Error: Unknown preset '" + preset_name + "'")
    Some(g) => {
      let b_rep = @analysis.analyze_production_bottlenecks(g)
      let spofs = @algorithms.find_critical_spof_assets(g)
      let s_audit = @analysis.audit_plant_safety_systems(g)

      println("================ Production & Risk Analysis ================")
      println(
        "Primary Bottleneck Station : " +
        b_rep.primary_bottleneck_node_id +
        " (" +
        @types.format_double(b_rep.primary_bottleneck_cycle_time, 1) +
        "s)",
      )
      println(
        "Line Balancing Efficiency  : " +
        @types.format_double(b_rep.line_balance_efficiency_percent, 1) +
        "%",
      )
      println(
        "Max Capacity Ceiling       : " +
        @types.format_double(b_rep.max_theoretical_hourly_output, 1) +
        " units/hour",
      )
      println(
        "Single Points of Failure   : " +
        spofs.single_points_of_failure.length().to_string() +
        " assets",
      )
      for s in spofs.single_points_of_failure {
        println("  - SPOF: " + s)
      }
      println("Safety Compliance Grade    : " + s_audit.audit_status)
      println(
        "E-Stop Protection Coverage : " +
        @types.format_double(s_audit.estop_coverage_percent, 1) +
        "%",
      )
    }
  }
}

///|
fn cmd_outage(preset_name : String, node_id : String) -> Unit {
  match get_preset_graph(preset_name) {
    None => println("Error: Unknown preset '" + preset_name + "'")
    Some(g) => {
      if !g.has_node(node_id) {
        println("Error: Node '" + node_id + "' not found in plant topology.")
        return
      }
      let res = @analysis.simulate_device_outage(g, node_id)
      println(
        "================ Device Outage Cascade Simulation ================",
      )
      println("Failed Root Asset     : " + res.failed_node_id)
      println(
        "Power Blackout Nodes  : " +
        res.power_blackout_nodes.length().to_string(),
      )
      println(
        "PLC Disconnect Nodes  : " + res.plc_timeout_nodes.length().to_string(),
      )
      println(
        "Safety Interlock Trips: " +
        res.safety_tripped_nodes.length().to_string(),
      )
      for sn in res.safety_tripped_nodes {
        println("  * Tripped E-Stop: " + sn)
      }
      println(
        "Starved Workstations  : " +
        res.starved_workstations.length().to_string(),
      )
      println(
        "Blocked Workstations  : " +
        res.blocked_workstations.length().to_string(),
      )
      println(
        "Capacity Loss         : " +
        @types.format_double(res.production_capacity_loss_percent, 1) +
        "%",
      )
      println(
        "Est. Financial Loss   : " +
        @types.format_double(res.estimated_hourly_loss_cny, 2) +
        " CNY/hour",
      )
    }
  }
}

///|
fn cmd_diff(p1 : String, p2 : String) -> Unit {
  let g1_opt = get_preset_graph(p1)
  let g2_opt = get_preset_graph(p2)
  match (g1_opt, g2_opt) {
    (Some(g1), Some(g2)) => {
      let report = @diff.compare_factory_topologies(g1, g2)
      println(
        "================ Topology Drift & Risk Evaluation ================",
      )
      println("Risk Level           : " + report.overall_risk_level.to_string())
      println(
        "Risk Score           : " +
        @types.format_double(report.risk_score, 1) +
        " / 100",
      )
      println(
        "Added Nodes          : " + report.added_nodes.length().to_string(),
      )
      println(
        "Removed Nodes        : " + report.removed_nodes.length().to_string(),
      )
      println(
        "Modified Nodes       : " + report.modified_nodes.length().to_string(),
      )
      println(
        "Breaking Changes     : " + report.breaking_changes.length().to_string(),
      )
      for bc in report.breaking_changes {
        println("  ! " + bc)
      }
    }
    _ => println("Error: Presets '" + p1 + "' and/or '" + p2 + "' not found.")
  }
}

///|
fn cmd_demo() -> Unit {
  print_banner()
  println(
    "Running Comprehensive Multi-Plant Digital Twin Simulation Demonstration...\n",
  )

  println("1. Instantiating Automotive Manufacturing Gigafactory...")
  let auto_g = @presets.build_automotive_plant()
  println(
    "   -> Created " +
    auto_g.node_count().to_string() +
    " assets, " +
    auto_g.edge_count().to_string() +
    " flow edges.",
  )

  println("2. Calculating Optimal End-to-End Material Flow...")
  let p = @algorithms.dijkstra_shortest_path(
    auto_g, "STAMP-UNCOIL", "ASSY-INSPECT",
  )
  match p {
    Some(res) => {
      println(
        "   -> Found critical production route: " +
        res.nodes.length().to_string() +
        " stations.",
      )
      println(
        "   -> Cumulative transport delay: " +
        @types.format_double(res.total_delay_sec, 1) +
        "s",
      )
      println(
        "   -> Line bottleneck bandwidth : " +
        @types.format_double(res.bottleneck_bandwidth, 1) +
        " units/h",
      )
    }
    None => println("   -> No path found.")
  }

  println("3. Simulating Catastrophic Substation Failure...")
  let outage = @analysis.simulate_device_outage(auto_g, "SUB-02")
  println(
    "   -> Affected Workstations: " +
    outage.affected_workstations_count.to_string(),
  )
  println(
    "   -> Production Line Capacity Loss: " +
    @types.format_double(outage.production_capacity_loss_percent, 1) +
    "%",
  )

  println("4. Generating Plant Operations Executive Audit Report...")
  let rep = @export.generate_executive_audit_report(auto_g)
  println(
    "   -> Generated " +
    rep.length().to_string() +
    " bytes Markdown report successfully.\n",
  )

  println("Demo execution completed successfully.")
}

///|

///|
fn build_benchmark_graph() -> @graph.FactoryGraph {
  let builder = @graph.FactoryGraphBuilder::new(
    plant_name="BenchmarkPlant",
    takt_time_target_sec=30.0,
  )
  for i = 1; i <= 1000; i = i + 1 {
    let _ = builder.add_workstation(
      "N-" + i.to_string(),
      "Machine " + i.to_string(),
      cycle_time_sec=20.0,
      nominal_power_kw=15.0,
    )
  }
  // A chain plus skip links gives the benchmark both serial and alternate
  // routing pressure while keeping the topology deterministic.
  for i = 1; i < 1000; i = i + 1 {
    let _ = builder.connect_material(
      "N-" + i.to_string(),
      "N-" + (i + 1).to_string(),
      bandwidth=1200.0,
    )
  }
  for i = 1; i < 999; i = i + 1 {
    let _ = builder.connect_material(
      "N-" + i.to_string(),
      "N-" + (i + 2).to_string(),
      bandwidth=900.0,
      delay=1.0,
    )
  }
  builder.build()
}

///|
fn cmd_bench() -> Unit {
  println("Running Industrial Graph Scaling Benchmark...")
  let graph = build_benchmark_graph()
  println(
    "Benchmark Graph Built: " +
    graph.node_count().to_string() +
    " nodes, " +
    graph.edge_count().to_string() +
    " material edges.",
  )

  let bench = @bench.Bench::Bench()
  bench.bench(
    name="build_1000_nodes_1997_edges",
    () => {
      let candidate = build_benchmark_graph()
      bench.keep(candidate.node_count() + candidate.edge_count())
    },
    count=5,
  )
  bench.bench(
    name="dijkstra_1000_nodes",
    () => {
      match @algorithms.dijkstra_shortest_path(graph, "N-1", "N-1000") {
        Some(result) => bench.keep(result.nodes.length())
        None => bench.keep(0)
      }
    },
    count=5,
  )
  bench.bench(
    name="spof_biconnectivity_1000_nodes",
    () => {
      let report = @algorithms.find_critical_spof_assets(graph)
      bench.keep(report.single_points_of_failure.length())
    },
    count=5,
  )
  bench.bench(
    name="capacity_and_health_report",
    () => {
      let capacity = @analysis.analyze_material_capacity(
        graph,
        demand_per_hour=120.0,
      )
      let health = @graph.inspect_graph_health(graph)
      bench.keep(
        capacity.overloaded_edge_ids.length() + health.active_edge_count,
      )
    },
    count=5,
  )
  println("Measured summaries (microseconds per operation):")
  println(bench.dump_summaries())
  println("Benchmark complete.")
}

///|
fn main {
  let args = @env.args()
  if args.length() <= 1 {
    cmd_demo()
    return
  }

  let cmd = args[1]
  match cmd {
    "info" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      cmd_info(preset)
    }
    "health" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      cmd_health(preset)
    }
    "capacity" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      cmd_capacity(preset)
    }
    "readiness" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      cmd_readiness(preset)
    }
    "analyze" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      cmd_analyze(preset)
    }
    "outage" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      let node_id = if args.length() > 3 { args[3] } else { "WELD-FLOOR" }
      cmd_outage(preset, node_id)
    }
    "diff" => {
      let p1 = if args.length() > 2 { args[2] } else { "automotive" }
      let p2 = if args.length() > 3 { args[3] } else { "battery" }
      cmd_diff(p1, p2)
    }
    "path" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      let src = if args.length() > 3 { args[3] } else { "STAMP-UNCOIL" }
      let dst = if args.length() > 4 { args[4] } else { "ASSY-INSPECT" }
      match get_preset_graph(preset) {
        None => println("Error: Unknown preset '" + preset + "'")
        Some(g) => {
          let p = @algorithms.dijkstra_shortest_path(g, src, dst)
          match p {
            Some(res) => {
              println(
                "Path Found: " + res.nodes.length().to_string() + " nodes",
              )
              for n in res.nodes {
                println("  -> " + n)
              }
              println("Total Delay: " + res.total_delay_sec.to_string() + "s")
              println(
                "Bottleneck Bandwidth: " +
                res.bottleneck_bandwidth.to_string() +
                " u/h",
              )
            }
            None => println("No path found between " + src + " and " + dst)
          }
        }
      }
    }
    "export" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      let fmt = if args.length() > 3 { args[3] } else { "dot" }
      match get_preset_graph(preset) {
        None => println("Error: Unknown preset '" + preset + "'")
        Some(g) => {
          let out = match fmt {
            "dot" => @export.export_to_graphviz_dot(g)
            "mermaid" => @export.export_to_mermaid(g)
            "puml" => @export.export_to_plantuml(g)
            "json" => @export.export_to_json(g)
            "report" => @export.generate_executive_audit_report(g)
            "nodes_csv" => @export.export_nodes_csv(g)
            "edges_csv" => @export.export_edges_csv(g)
            _ => @export.export_to_graphviz_dot(g)
          }
          println(out)
        }
      }
    }
    "optimize" => {
      let preset = if args.length() > 2 { args[2] } else { "automotive" }
      match get_preset_graph(preset) {
        None => println("Error: Unknown preset '" + preset + "'")
        Some(g) => {
          let plan = @optimizer.optimize_line_balance(g)
          println(
            "Line Balancing Recommendations: " +
            plan.recommendations.length().to_string(),
          )
          for rec in plan.recommendations {
            println(
              "  - Station " +
              rec.station_id +
              ": " +
              rec.suggested_action +
              " (Current CT: " +
              rec.current_cycle_time.to_string() +
              "s)",
            )
          }
        }
      }
    }
    "demo" => cmd_demo()
    "bench" => cmd_bench()
    "help" | "-h" | "--help" => print_help()
    _ => {
      println("Unknown command: " + cmd)
      print_help()
    }
  }
}