///|
pub(all) enum PassportBand {
  Excellent
  Solid
  Watchlist
  Rework
} derive(Eq, Debug)

///|
pub(all) struct RetrofitPassport {
  site_name : String
  event_name : String
  band : PassportBand
  overall_score : Int
  footprint_area_m2 : Double
  runoff_volume_m3 : Double
  captured_volume_m3 : Double
  bypass_volume_m3 : Double
  capture_ratio : Double
  capital_cost : Double
  annual_maintenance_cost : Double
  maintenance_priority : MaintenancePriority
  maintenance_interval_days : Int
  public_summary : String
  field_actions : Array[String]
  assumptions : Array[String]
} derive(Eq, Debug)

///|
pub fn build_retrofit_passport(
  site_name : String,
  garden : GardenPlan,
  train : TreatmentTrainPlan,
  cost : TrainCostSummary,
  maintenance : MaintenancePlan,
) -> RetrofitPassport {
  let score = passport_score(garden, train, cost, maintenance)
  let assumptions = passport_assumptions(garden, train, cost)
  let actions = passport_actions(train, cost, maintenance)
  let band = passport_band(
    score,
    train.capture_ratio,
    train.bypass_volume_m3,
    maintenance.priority,
  )
  {
    site_name,
    event_name: garden.event_name,
    band,
    overall_score: score,
    footprint_area_m2: train.footprint_area_m2,
    runoff_volume_m3: garden.runoff_volume_m3,
    captured_volume_m3: train.total_captured_m3,
    bypass_volume_m3: train.bypass_volume_m3,
    capture_ratio: train.capture_ratio,
    capital_cost: cost.total_capital_cost,
    annual_maintenance_cost: cost.annual_maintenance_cost,
    maintenance_priority: maintenance.priority,
    maintenance_interval_days: maintenance.interval_days,
    public_summary: passport_summary(
      site_name,
      band,
      train.capture_ratio,
      train.bypass_volume_m3,
    ),
    field_actions: actions,
    assumptions,
  }
}

///|
pub fn scenario_passport(
  site_name : String,
  scenario : ScenarioResult,
) -> RetrofitPassport {
  let band = passport_band(
    scenario.overall_score,
    scenario.treatment_capture_ratio,
    scenario.treatment_bypass_m3,
    scenario.maintenance_priority,
  )
  let actions : Array[String] = []
  if scenario.status != Ready {
    actions.push("review scenario before using it as the preferred plan")
  }
  if scenario.treatment_bypass_m3 > 0.0 {
    actions.push("identify a safe overflow route for remaining bypass")
  }
  if scenario.maintenance_priority == ServiceSoon ||
    scenario.maintenance_priority == Restore {
    actions.push("schedule maintenance before relying on the scenario")
  }
  if scenario.budget_fit_score < 50 {
    actions.push("revise scope or budget assumptions")
  }
  if actions.length() == 0 {
    actions.push("record baseline photos after construction")
  }
  {
    site_name,
    event_name: scenario.name,
    band,
    overall_score: scenario.overall_score,
    footprint_area_m2: scenario.recommended_area_m2,
    runoff_volume_m3: scenario.adjusted_runoff_m3,
    captured_volume_m3: scenario.adjusted_runoff_m3 *
    scenario.treatment_capture_ratio,
    bypass_volume_m3: scenario.treatment_bypass_m3,
    capture_ratio: scenario.treatment_capture_ratio,
    capital_cost: scenario.total_capital_cost,
    annual_maintenance_cost: 0.0,
    maintenance_priority: scenario.maintenance_priority,
    maintenance_interval_days: passport_interval_from_priority(
      scenario.maintenance_priority,
    ),
    public_summary: passport_summary(
      site_name,
      band,
      scenario.treatment_capture_ratio,
      scenario.treatment_bypass_m3,
    ),
    field_actions: actions,
    assumptions: scenario.notes,
  }
}

///|
pub fn passport_followup_days(passport : RetrofitPassport) -> Int {
  let base = passport.maintenance_interval_days
  if passport.band == Rework {
    passport_min_int(base, 14)
  } else if passport.band == Watchlist {
    passport_min_int(base, 30)
  } else if passport.maintenance_priority == Watch {
    passport_min_int(base, 45)
  } else {
    base
  }
}

///|
pub fn passport_handoff_checklist(passport : RetrofitPassport) -> Array[String] {
  let checklist : Array[String] = []
  checklist.push("save the design storm and catchment assumptions")
  checklist.push("photograph inlet, overflow, and vegetation zones")
  if passport.bypass_volume_m3 > 0.0 {
    checklist.push("mark the safe bypass path on the site sketch")
  }
  if passport.capital_cost > 0.0 {
    checklist.push("attach the cost profile used for this passport")
  }
  if passport.maintenance_priority != Routine {
    checklist.push("schedule the first maintenance follow-up")
  }
  for action in passport.field_actions {
    checklist.push(action)
  }
  checklist
}

///|
pub fn PassportBand::label(self : PassportBand) -> String {
  match self {
    Excellent => "excellent"
    Solid => "solid"
    Watchlist => "watchlist"
    Rework => "rework"
  }
}

///|
pub fn RetrofitPassport::to_json(self : RetrofitPassport) -> String {
  let out = StringBuilder::new()
  out.write_string("{")
  out.write_string("\"site\":\"" + escape_json(self.site_name) + "\",")
  out.write_string("\"event\":\"" + escape_json(self.event_name) + "\",")
  out.write_string("\"band\":\"" + self.band.label() + "\",")
  out.write_string("\"overall_score\":" + self.overall_score.to_string() + ",")
  out.write_string(
    "\"footprint_area_m2\":" + self.footprint_area_m2.to_string() + ",",
  )
  out.write_string(
    "\"runoff_volume_m3\":" + self.runoff_volume_m3.to_string() + ",",
  )
  out.write_string(
    "\"captured_volume_m3\":" + self.captured_volume_m3.to_string() + ",",
  )
  out.write_string(
    "\"bypass_volume_m3\":" + self.bypass_volume_m3.to_string() + ",",
  )
  out.write_string("\"capture_ratio\":" + self.capture_ratio.to_string() + ",")
  out.write_string("\"capital_cost\":" + self.capital_cost.to_string() + ",")
  out.write_string(
    "\"annual_maintenance_cost\":" +
    self.annual_maintenance_cost.to_string() +
    ",",
  )
  out.write_string(
    "\"maintenance_priority\":\"" + self.maintenance_priority.label() + "\",",
  )
  out.write_string(
    "\"maintenance_interval_days\":" +
    self.maintenance_interval_days.to_string() +
    ",",
  )
  out.write_string(
    "\"public_summary\":\"" + escape_json(self.public_summary) + "\",",
  )
  out.write_string(
    "\"field_actions\":" +
    passport_string_array_to_json(self.field_actions) +
    ",",
  )
  out.write_string(
    "\"assumptions\":" + passport_string_array_to_json(self.assumptions),
  )
  out.write_string("}")
  out.to_string()
}

///|
pub fn RetrofitPassport::to_markdown(self : RetrofitPassport) -> String {
  let out = StringBuilder::new()
  out.write_string("# SpongeKit Retrofit Passport\n\n")
  out.write_string("- Site: " + self.site_name + "\n")
  out.write_string("- Event: " + self.event_name + "\n")
  out.write_string("- Band: " + self.band.label() + "\n")
  out.write_string("- Score: " + self.overall_score.to_string() + "\n")
  out.write_string(
    "- Footprint: " + self.footprint_area_m2.to_string() + " m2\n",
  )
  out.write_string(
    "- Captured: " + self.captured_volume_m3.to_string() + " m3\n",
  )
  out.write_string("- Bypass: " + self.bypass_volume_m3.to_string() + " m3\n")
  out.write_string(
    "- Maintenance: " + self.maintenance_priority.label() + "\n\n",
  )
  out.write_string(self.public_summary + "\n\n")
  out.write_string("## Field Actions\n\n")
  for action in self.field_actions {
    out.write_string("- " + action + "\n")
  }
  if self.assumptions.length() > 0 {
    out.write_string("\n## Assumptions\n\n")
    for assumption in self.assumptions {
      out.write_string("- " + assumption + "\n")
    }
  }
  out.to_string()
}

///|
fn passport_score(
  garden : GardenPlan,
  train : TreatmentTrainPlan,
  cost : TrainCostSummary,
  maintenance : MaintenancePlan,
) -> Int {
  let capture_points = (passport_clamp_double(train.capture_ratio, 0.0, 1.0) *
  42.0).to_int()
  let readiness_points = if garden.status == Ready && train.status == Ready {
    18
  } else if garden.status != Invalid && train.status != Invalid {
    8
  } else {
    0
  }
  let cost_points = if cost.cost_per_captured_m3 <= 12000.0 {
    18
  } else if cost.cost_per_captured_m3 <= 25000.0 {
    12
  } else if cost.cost_per_captured_m3 <= 50000.0 {
    6
  } else {
    0
  }
  let resilience_points = (train.resilience_score.to_double() * 0.18).to_int()
  passport_clamp_int(
    capture_points +
    readiness_points +
    cost_points +
    resilience_points -
    maintenance_penalty_for_passport(maintenance.priority),
    0,
    100,
  )
}

///|
fn passport_band(
  score : Int,
  capture_ratio : Double,
  bypass_volume_m3 : Double,
  priority : MaintenancePriority,
) -> PassportBand {
  if score >= 85 && capture_ratio >= 0.95 && bypass_volume_m3 <= 0.25 {
    Excellent
  } else if score >= 70 && priority != Restore {
    Solid
  } else if score >= 45 && priority != Restore {
    Watchlist
  } else {
    Rework
  }
}

///|
fn passport_assumptions(
  garden : GardenPlan,
  train : TreatmentTrainPlan,
  cost : TrainCostSummary,
) -> Array[String] {
  let assumptions : Array[String] = []
  assumptions.push(
    "design rain depth " + garden.design_rain_mm.to_string() + " mm",
  )
  assumptions.push(
    "catchment area " + garden.catchment_area_m2.to_string() + " m2",
  )
  assumptions.push(
    "treatment train facility count " + train.facility_count.to_string(),
  )
  assumptions.push("cost currency " + cost.currency)
  if train.notes.length() > 0 {
    for note in train.notes {
      assumptions.push(note)
    }
  }
  if cost.notes.length() > 0 {
    for note in cost.notes {
      assumptions.push(note)
    }
  }
  assumptions
}

///|
fn passport_actions(
  train : TreatmentTrainPlan,
  cost : TrainCostSummary,
  maintenance : MaintenancePlan,
) -> Array[String] {
  let actions : Array[String] = []
  for action in maintenance.actions {
    actions.push(action.label())
  }
  if train.bypass_volume_m3 > 0.0 {
    actions.push("confirm overflow route before construction")
  }
  if cost.notes.length() > 0 {
    actions.push("review cost notes before procurement")
  }
  if actions.length() == 0 {
    actions.push("keep routine inspection log after major storms")
  }
  actions
}

///|
fn passport_summary(
  site_name : String,
  band : PassportBand,
  capture_ratio : Double,
  bypass_volume_m3 : Double,
) -> String {
  site_name +
  " is rated " +
  band.label() +
  " with capture ratio " +
  capture_ratio.to_string() +
  " and bypass " +
  bypass_volume_m3.to_string() +
  " m3 under the selected design event."
}

///|
fn passport_interval_from_priority(priority : MaintenancePriority) -> Int {
  match priority {
    Routine => 90
    Watch => 60
    ServiceSoon => 30
    Restore => 14
  }
}

///|
fn maintenance_penalty_for_passport(priority : MaintenancePriority) -> Int {
  match priority {
    Routine => 0
    Watch => 5
    ServiceSoon => 14
    Restore => 28
  }
}

///|
fn passport_min_int(left : Int, right : Int) -> Int {
  if left < right {
    left
  } else {
    right
  }
}

///|
fn passport_clamp_double(value : Double, low : Double, high : Double) -> Double {
  if value < low {
    low
  } else if value > high {
    high
  } else {
    value
  }
}

///|
fn passport_clamp_int(value : Int, low : Int, high : Int) -> Int {
  if value < low {
    low
  } else if value > high {
    high
  } else {
    value
  }
}

///|
fn passport_string_array_to_json(values : Array[String]) -> String {
  let out = StringBuilder::new()
  out.write_string("[")
  for index, value in values {
    if index > 0 {
      out.write_string(",")
    }
    out.write_string("\"" + escape_json(value) + "\"")
  }
  out.write_string("]")
  out.to_string()
}