///|
pub enum WorkflowStatus {
  Draft
  Review
  Approved
  Rejected
  Archived
} derive(Debug, Eq)

///|
pub struct WorkflowStep {
  name : String
  owner : String
  status : WorkflowStatus
  note : String
} derive(Debug, Eq)

///|
pub fn workflow_step(
  name : String,
  owner : String,
  status : WorkflowStatus,
  note : String,
) -> WorkflowStep {
  { name, owner, status, note }
}

///|
pub fn WorkflowStep::is_complete(self : WorkflowStep) -> Bool {
  self.status is Approved || self.status is Archived
}

///|
pub fn archive_step(
  name : String,
  owner : String,
  note : String,
) -> WorkflowStep {
  workflow_step(name, owner, Archived, note)
}

///|
pub struct ReportWorkflow {
  id : String
  steps : Array[WorkflowStep]
  current : Int
} derive(Debug, Eq)

///|
pub fn report_workflow(
  id : String,
  steps : Array[WorkflowStep],
) -> ReportWorkflow {
  { id, steps, current: 0 }
}

///|
pub fn ReportWorkflow::count(self : ReportWorkflow) -> Int {
  self.steps.length()
}

///|
pub fn ReportWorkflow::current_step(self : ReportWorkflow) -> WorkflowStep? {
  self.steps.get(self.current)
}

///|
pub fn ReportWorkflow::completed_count(self : ReportWorkflow) -> Int {
  let mut count = 0
  for step in self.steps {
    if step.is_complete() {
      count = count + 1
    }
  }
  count
}

///|
pub fn ReportWorkflow::progress(self : ReportWorkflow) -> Float {
  if self.count() == 0 {
    0.0
  } else {
    Float::from_int(self.completed_count()) / Float::from_int(self.count())
  }
}

///|
pub fn ReportWorkflow::is_complete(self : ReportWorkflow) -> Bool {
  self.count() > 0 && self.completed_count() == self.count()
}

///|
pub fn ReportWorkflow::advance(self : ReportWorkflow) -> ReportWorkflow {
  if self.current < self.steps.length() - 1 {
    { ..self, current: self.current + 1 }
  } else {
    self
  }
}

///|
pub fn ReportWorkflow::with_step(
  self : ReportWorkflow,
  step : WorkflowStep,
) -> ReportWorkflow {
  let steps = self.steps.copy()
  steps.push(step)
  { ..self, steps, }
}

///|
pub fn ReportWorkflow::approve_current(
  self : ReportWorkflow,
  note : String,
) -> ReportWorkflow {
  if self.current_step() is Some(current) {
    let steps = self.steps.copy()
    steps[self.current] = { ..current, status: Approved, note }
    { ..self, steps, }
  } else {
    self
  }
}

///|
pub fn ReportWorkflow::reject_current(
  self : ReportWorkflow,
  note : String,
) -> ReportWorkflow {
  if self.current_step() is Some(current) {
    let steps = self.steps.copy()
    steps[self.current] = { ..current, status: Rejected, note }
    { ..self, steps, }
  } else {
    self
  }
}

///|
pub fn ReportWorkflow::to_table(self : ReportWorkflow) -> ReportTable {
  let rows : Array[Array[String]] = []
  for index, step in self.steps {
    let status = match step.status {
      Draft => "draft"
      Review => "review"
      Approved => "approved"
      Rejected => "rejected"
      Archived => "archived"
    }
    rows.push(["\{index}", step.name, step.owner, status, step.note])
  }
  table(["step", "name", "owner", "status", "note"], rows)
}

///|
pub fn ReportWorkflow::to_section(self : ReportWorkflow) -> ReportSection {
  { title: self.id, kind: Notes, body: self.to_table().to_markdown() }
}

///|
pub fn initial_workflow(id : String, owner : String) -> ReportWorkflow {
  report_workflow(id, [
    workflow_step("draft", owner, Draft, ""),
    workflow_step("technical-review", owner, Review, ""),
    workflow_step("acceptance", owner, Review, ""),
  ])
}

///|
pub fn workflow_gate(workflow : ReportWorkflow) -> Bool {
  workflow.is_complete()
}

///|
pub struct AuditEvent {
  timestamp : String
  actor : String
  action : String
  object : String
} derive(Debug, Eq)

///|
pub fn audit_event(
  timestamp : String,
  actor : String,
  action : String,
  object : String,
) -> AuditEvent {
  { timestamp, actor, action, object }
}

///|
pub struct AuditTrail {
  events : Array[AuditEvent]
} derive(Debug, Eq)

///|
pub fn audit_trail(events : Array[AuditEvent]) -> AuditTrail {
  { events, }
}

///|
pub fn AuditTrail::count(self : AuditTrail) -> Int {
  self.events.length()
}

///|
pub fn AuditTrail::append(self : AuditTrail, event : AuditEvent) -> AuditTrail {
  let events = self.events.copy()
  events.push(event)
  { events, }
}

///|
pub fn AuditTrail::actors(self : AuditTrail) -> Array[String] {
  let result : Array[String] = []
  for event in self.events {
    if !result.contains(event.actor) {
      result.push(event.actor)
    }
  }
  result
}

///|
pub fn AuditTrail::actions(self : AuditTrail) -> Array[String] {
  self.events.map(fn(event) { event.action })
}

///|
pub fn AuditTrail::to_table(self : AuditTrail) -> ReportTable {
  let rows : Array[Array[String]] = []
  for event in self.events {
    rows.push([event.timestamp, event.actor, event.action, event.object])
  }
  table(["timestamp", "actor", "action", "object"], rows)
}

///|
pub fn AuditTrail::to_markdown(self : AuditTrail) -> String {
  self.to_table().to_markdown()
}

///|
pub fn audit_quality_gate(trail : AuditTrail) -> Bool {
  trail.count() > 0 && trail.actors().length() > 0
}