///|
pub enum ProvenanceError {
  InvalidStage
  StageOutOfOrder
  EmptyActor
  EmptyDetail
} derive(Debug, Eq)

///|
pub struct ProvenanceEvent {
  stage : String
  actor : String
  detail : String
} derive(Debug, Eq)

///|
pub struct ProvenanceLedger {
  report_id : String
  entries : Array[ProvenanceEvent]
} derive(Debug, Eq)

///|
pub fn provenance_event(
  stage : String,
  actor : String,
  detail : String,
) -> ProvenanceEvent {
  { stage, actor, detail }
}

///|
pub fn provenance_ledger(report_id : String) -> ProvenanceLedger {
  { report_id, entries: [] }
}

///|
fn provenance_rank(stage : String) -> Int? {
  match stage {
    "created" => Some(0)
    "validated" => Some(1)
    "rendered" | "exported" => Some(2)
    _ => None
  }
}

///|
pub fn ProvenanceLedger::record(
  self : ProvenanceLedger,
  event : ProvenanceEvent,
) -> Result[ProvenanceLedger, ProvenanceError] {
  if event.actor.trim() == "" {
    return Err(EmptyActor)
  }
  if event.detail.trim() == "" {
    return Err(EmptyDetail)
  }
  let rank = provenance_rank(event.stage)
  if rank is None {
    return Err(InvalidStage)
  }
  if self.entries.length() == 0 && rank.unwrap() != 0 {
    return Err(StageOutOfOrder)
  }
  if self.entries.length() > 0 {
    let previous = provenance_rank(
      self.entries[self.entries.length() - 1].stage,
    )
    if previous is None || rank.unwrap() < previous.unwrap() {
      return Err(StageOutOfOrder)
    }
  }
  let entries = self.entries.copy()
  entries.push(event)
  Ok({ report_id: self.report_id, entries })
}

///|
pub fn ProvenanceLedger::events(
  self : ProvenanceLedger,
) -> Array[ProvenanceEvent] {
  self.entries.copy()
}

///|
pub fn ProvenanceLedger::is_complete(self : ProvenanceLedger) -> Bool {
  let mut validated = false
  let mut exported = false
  for item in self.entries {
    if item.stage == "validated" {
      validated = true
    }
    if item.stage == "rendered" || item.stage == "exported" {
      exported = true
    }
  }
  validated && exported
}