///|
pub struct EmissionFactor {
  pollutant : String
  factor : Float
  unit : String
  source : String
} derive(Debug, Eq)

///|
pub fn emission_factor(
  pollutant : String,
  factor : Float,
  unit : String,
  source : String,
) -> EmissionFactor {
  { pollutant, factor, unit, source }
}

///|
pub struct Emission {
  pollutant : String
  amount : Float
  unit : String
  source : String
} derive(Debug, Eq)

///|
pub fn emission(
  pollutant : String,
  amount : Float,
  unit : String,
  source : String,
) -> Emission {
  { pollutant, amount, unit, source }
}

///|
pub fn Emission::is_valid(self : Emission) -> Bool {
  self.pollutant.trim().length() > 0 && self.amount >= 0.0
}

///|
pub struct EnvironmentalInventory {
  emissions : Array[Emission]
  period : String
  facility : String
} derive(Debug, Eq)

///|
pub fn environmental_inventory(
  emissions : Array[Emission],
  period : String,
  facility : String,
) -> EnvironmentalInventory {
  { emissions, period, facility }
}

///|
pub fn EnvironmentalInventory::total(self : EnvironmentalInventory) -> Float {
  let mut total : Float = 0.0
  for item in self.emissions {
    total = total + item.amount
  }
  total
}

///|
pub fn EnvironmentalInventory::pollutants(
  self : EnvironmentalInventory,
) -> Array[String] {
  let result : Array[String] = []
  for item in self.emissions {
    if !result.contains(item.pollutant) {
      result.push(item.pollutant)
    }
  }
  result
}

///|
pub fn EnvironmentalInventory::by_pollutant(
  self : EnvironmentalInventory,
  pollutant : String,
) -> Float {
  let mut total : Float = 0.0
  for item in self.emissions {
    if item.pollutant == pollutant {
      total = total + item.amount
    }
  }
  total
}

///|
pub fn EnvironmentalInventory::table(
  self : EnvironmentalInventory,
) -> ReportTable {
  let rows : Array[Array[String]] = []
  for pollutant in self.pollutants() {
    rows.push([
      pollutant,
      "{self.by_pollutant(pollutant)}",
      self.period,
      self.facility,
    ])
  }
  table(["pollutant", "amount", "period", "facility"], rows)
}

///|
pub fn EnvironmentalInventory::valid(self : EnvironmentalInventory) -> Bool {
  for item in self.emissions {
    if !item.is_valid() {
      return false
    }
  }
  true
}

///|
pub fn carbon_emission(energy : Float, factor : EmissionFactor) -> Emission {
  emission(factor.pollutant, energy * factor.factor, factor.unit, factor.source)
}

///|
pub fn emission_intensity(
  emissions : EnvironmentalInventory,
  production : Float,
) -> Float {
  if production <= 0.0 {
    0.0
  } else {
    emissions.total() / production
  }
}

///|
pub fn reduction_target(baseline : Float, reduction : Float) -> Float {
  baseline * (1.0 - clamp(reduction, 0.0, 1.0))
}

///|
pub fn reduction_achieved(baseline : Float, current : Float) -> Float {
  if baseline <= 0.0 {
    0.0
  } else {
    clamp((baseline - current) / baseline, 0.0, 1.0)
  }
}

///|
pub fn environmental_gate(
  inventory : EnvironmentalInventory,
  maximum : Float,
) -> Bool {
  inventory.valid() && inventory.total() <= maximum
}

///|
pub fn environmental_section(
  inventory : EnvironmentalInventory,
) -> ReportSection {
  {
    title: "Environmental inventory",
    kind: Safety,
    body: inventory.table().to_markdown(),
  }
}