///|
/// Severity used by an operational workflow journal.
pub(all) enum JournalLevel {
  Info
  Warning
  Error
  Critical
} derive(Eq)

///|
/// One immutable-facing entry in an operational audit stream.
pub(all) struct JournalEntry {
  sequence : Int
  ticket_id : String
  state : String
  event : String
  level : JournalLevel
  message : String
  mut acknowledged : Bool
}

///|
/// An in-memory journal suitable for deterministic tests and adapters.
pub(all) struct WorkflowJournal {
  mut entries : Array[JournalEntry]
  mut next_sequence : Int
}

///|
/// Creates an empty journal with sequence numbers starting at one.
pub fn WorkflowJournal::new() -> WorkflowJournal {
  { entries: [], next_sequence: 1 }
}

///|
fn WorkflowJournal::append_entry(
  self : WorkflowJournal,
  ticket_id : String,
  state : String,
  event : String,
  level : JournalLevel,
  message : String,
) -> Int {
  let sequence = self.next_sequence
  self.entries.push({
    sequence,
    ticket_id,
    state,
    event,
    level,
    message,
    acknowledged: false,
  })
  self.next_sequence += 1
  sequence
}

///|
/// Appends an informational operational note.
pub fn WorkflowJournal::info(
  self : WorkflowJournal,
  ticket_id : String,
  state : String,
  event : String,
  message : String,
) -> Int {
  self.append_entry(ticket_id, state, event, Info, message)
}

///|
/// Appends a warning that may need operator follow-up.
pub fn WorkflowJournal::warning(
  self : WorkflowJournal,
  ticket_id : String,
  state : String,
  event : String,
  message : String,
) -> Int {
  self.append_entry(ticket_id, state, event, Warning, message)
}

///|
/// Appends a recoverable error.
pub fn WorkflowJournal::error(
  self : WorkflowJournal,
  ticket_id : String,
  state : String,
  event : String,
  message : String,
) -> Int {
  self.append_entry(ticket_id, state, event, Error, message)
}

///|
/// Appends a critical incident requiring explicit acknowledgement.
pub fn WorkflowJournal::critical(
  self : WorkflowJournal,
  ticket_id : String,
  state : String,
  event : String,
  message : String,
) -> Int {
  self.append_entry(ticket_id, state, event, Critical, message)
}

///|
/// Reads a defensive copy of the journal.
pub fn WorkflowJournal::entries(self : WorkflowJournal) -> Array[JournalEntry] {
  self.entries.copy()
}

///|
/// Acknowledges one entry and reports whether it existed.
pub fn WorkflowJournal::acknowledge(
  self : WorkflowJournal,
  sequence : Int,
) -> Bool {
  for index in 0.. Int {
  let mut acknowledged = 0
  for index in 0.. Int {
  let mut count = 0
  for entry in self.entries {
    if entry.level == level {
      count += 1
    }
  }
  count
}

///|
/// Counts unacknowledged error and critical entries.
pub fn WorkflowJournal::unacknowledged_incidents(self : WorkflowJournal) -> Int {
  let mut count = 0
  for entry in self.entries {
    if !entry.acknowledged && (entry.level == Error || entry.level == Critical) {
      count += 1
    }
  }
  count
}

///|
/// Returns entries belonging to a ticket in original sequence order.
pub fn WorkflowJournal::for_ticket(
  self : WorkflowJournal,
  ticket_id : String,
) -> Array[JournalEntry] {
  let result = []
  for entry in self.entries {
    if entry.ticket_id == ticket_id {
      result.push(entry)
    }
  }
  result
}

///|
/// Returns true when a ticket has a journal entry at a critical severity.
pub fn WorkflowJournal::has_critical(
  self : WorkflowJournal,
  ticket_id : String,
) -> Bool {
  for entry in self.entries {
    if entry.ticket_id == ticket_id && entry.level == Critical {
      return true
    }
  }
  false
}

///|
/// Adds all accepted and rejected events from a string-labelled FSM audit log.
pub fn WorkflowJournal::append_audit(
  self : WorkflowJournal,
  ticket_id : String,
  audit : Array[AuditRecord[String, String]],
) -> Int {
  let mut appended = 0
  for record in audit {
    let level = match record.error {
      None => Info
      Some(GuardRejected) => Warning
      Some(EventNotHandledInCurrentState) => Error
      Some(NoTransitionsForCurrentState) => Error
      Some(DuplicateTransitionDefinition) => Critical
      Some(InvalidConfiguration) => Critical
    }
    let message = match record.error {
      None => format_transition_log(record.from, record.event, record.to)
      Some(error) => format_transition_error(error)
    }
    ignore(
      self.append_entry(ticket_id, record.to, record.event, level, message),
    )
    appended += 1
  }
  appended
}

///|
/// Formats a compact line for console logs and line-oriented exporters.
pub fn journal_line(entry : JournalEntry) -> String {
  "#\{entry.sequence} [\{format_journal_level(entry.level)}] " +
  "\{entry.ticket_id} \{entry.state} \{entry.event}: \{entry.message}"
}

///|
pub fn format_journal_level(level : JournalLevel) -> String {
  match level {
    Info => "INFO"
    Warning => "WARN"
    Error => "ERROR"
    Critical => "CRITICAL"
  }
}

///|
/// A service-level target expressed in deterministic workflow steps.
pub(all) struct SlaTarget {
  max_steps : Int
  max_escalations : Int
} derive(Eq)

///|
/// Evaluation of a ticket against its service-level target.
pub(all) struct SlaEvaluation {
  within_steps : Bool
  within_escalations : Bool
  breached : Bool
  remaining_steps : Int
  remaining_escalations : Int
}

///|
/// Evaluates elapsed steps and escalation count without depending on a clock.
pub fn evaluate_sla(
  target : SlaTarget,
  elapsed_steps : Int,
  escalations : Int,
) -> SlaEvaluation {
  let remaining_steps = target.max_steps - elapsed_steps
  let remaining_escalations = target.max_escalations - escalations
  let within_steps = remaining_steps >= 0
  let within_escalations = remaining_escalations >= 0
  {
    within_steps,
    within_escalations,
    breached: !within_steps || !within_escalations,
    remaining_steps,
    remaining_escalations,
  }
}

///|
/// Returns a readable SLA status for dashboards.
pub fn sla_status(evaluation : SlaEvaluation) -> String {
  if evaluation.breached {
    "BREACHED"
  } else if evaluation.remaining_steps == 0 ||
    evaluation.remaining_escalations == 0 {
    "AT_LIMIT"
  } else {
    "WITHIN_TARGET"
  }
}

///|
/// A small aggregate for operational reporting across many tickets.
pub(all) struct JournalAggregate {
  tickets : Int
  entries : Int
  informational : Int
  warnings : Int
  errors : Int
  critical : Int
  unacknowledged : Int
}

///|
/// Aggregates a collection of ticket journals into one report.
pub fn aggregate_journals(
  journals : Array[WorkflowJournal],
) -> JournalAggregate {
  let ticket_ids = Map([])
  let mut entries = 0
  let mut informational = 0
  let mut warnings = 0
  let mut errors = 0
  let mut critical = 0
  let mut unacknowledged = 0
  for journal in journals {
    for entry in journal.entries {
      ticket_ids.set(entry.ticket_id, true)
      entries += 1
      if !entry.acknowledged {
        unacknowledged += 1
      }
      match entry.level {
        Info => informational += 1
        Warning => warnings += 1
        Error => errors += 1
        Critical => critical += 1
      }
    }
  }
  {
    tickets: ticket_ids.length(),
    entries,
    informational,
    warnings,
    errors,
    critical,
    unacknowledged,
  }
}