///|
pub(all) enum ValidationLevel {
  Info
  Warning
  Error
} derive(Debug, Eq, ToJson)

///|
pub(all) struct ValidationMessage {
  level : ValidationLevel
  field : String
  message : String
} derive(Debug, ToJson)

///|
pub(all) struct ValidationReport {
  messages : Array[ValidationMessage]
} derive(Debug, ToJson)

///|
pub fn ValidationReport::new() -> ValidationReport {
  { messages: [] }
}

///|
pub fn ValidationReport::push(
  self : ValidationReport,
  level : ValidationLevel,
  field : String,
  message : String,
) -> ValidationReport {
  let messages = self.messages.copy()
  messages.push({ level, field, message })
  { messages, }
}

///|
pub fn ValidationReport::has_errors(self : ValidationReport) -> Bool {
  for message in self.messages {
    if message.level == Error {
      break true
    }
  } nobreak {
    false
  }
}

///|
pub fn ValidationReport::count(
  self : ValidationReport,
  level : ValidationLevel,
) -> Int {
  for i = 0, n = 0; i < self.messages.length(); i = i + 1 {
    let message = self.messages[i]
    if message.level == level {
      continue i + 1, n + 1
    } else {
      continue i + 1, n
    }
  } nobreak {
    n
  }
}

///|
pub fn validate_reaction(reaction : Reaction) -> ValidationReport {
  let report = ValidationReport::new()
  let report = if reaction.name.length() == 0 {
    report.push(Warning, "name", "reaction name is empty")
  } else {
    report
  }
  let report = if reaction.k_ref <= 0.0 {
    report.push(Error, "k_ref", "rate constant must be positive")
  } else {
    report
  }
  let report = if reaction.reference_temperature <= 0.0 {
    report.push(Error, "reference_temperature", "temperature must use Kelvin")
  } else {
    report
  }
  if reaction.activation_energy < 0.0 {
    report.push(
      Warning,
      "activation_energy",
      "negative activation energy is unusual",
    )
  } else {
    report
  }
}

///|
pub fn validate_feed(feed : Feed) -> ValidationReport {
  let report = ValidationReport::new()
  let report = if feed.concentration <= 0.0 {
    report.push(Error, "concentration", "feed concentration must be positive")
  } else {
    report
  }
  let report = if feed.volumetric_flow <= 0.0 {
    report.push(Error, "volumetric_flow", "volumetric flow must be positive")
  } else {
    report
  }
  let report = if feed.temperature <= 0.0 {
    report.push(Error, "temperature", "temperature must use Kelvin")
  } else {
    report
  }
  if feed.heat_capacity_flow <= 0.0 {
    report.push(
      Warning,
      "heat_capacity_flow",
      "non-positive heat-capacity flow will be guarded numerically",
    )
  } else {
    report
  }
}

///|
pub fn validate_design_inputs(
  reaction : Reaction,
  feed : Feed,
  volume : Double,
) -> ValidationReport {
  let r = validate_reaction(reaction)
  let f = validate_feed(feed)
  let messages = r.messages.copy()
  for message in f.messages {
    messages.push(message)
  }
  let report = { messages, }
  if volume < 0.0 {
    report.push(
      Error,
      "volume",
      "reactor volume or batch time proxy must be non-negative",
    )
  } else {
    report
  }
}

///|
pub fn ValidationLevel::to_string(level : ValidationLevel) -> String {
  match level {
    Info => "info"
    Warning => "warning"
    Error => "error"
  }
}

///|
pub fn ValidationMessage::to_line(self : ValidationMessage) -> String {
  "\{self.level.to_string()}:\{self.field}:\{self.message}"
}

///|
pub fn ValidationReport::to_text(self : ValidationReport) -> String {
  if self.messages.length() == 0 {
    "ok"
  } else {
    let builder = StringBuilder()
    for i, message in self.messages {
      if i > 0 {
        builder.write_string("\n")
      }
      builder.write_string(message.to_line())
    }
    builder.to_string()
  }
}