///|
pub(all) enum IncidentSeverity {
  Info
  Warning
  Critical
} derive(Eq, Debug)

///|
pub(all) struct Incident {
  id : String
  started_minute : Int
  ended_minute : Int
  severity : IncidentSeverity
  title : String
  impact_bp : Int
} derive(Eq, Debug)

///|
pub fn Incident::new(
  id : String,
  started_minute : Int,
  ended_minute : Int,
  severity : IncidentSeverity,
  title : String,
  impact_bp : Int,
) -> Incident {
  {
    id,
    started_minute,
    ended_minute: if ended_minute >= started_minute {
      ended_minute
    } else {
      started_minute
    },
    severity,
    title,
    impact_bp: if impact_bp < 0 {
      0
    } else if impact_bp > 10000 {
      10000
    } else {
      impact_bp
    },
  }
}

///|
pub fn Incident::duration(self : Incident) -> Int {
  self.ended_minute - self.started_minute + 1
}

///|
pub fn Incident::active_at(self : Incident, minute : Int) -> Bool {
  minute >= self.started_minute && minute <= self.ended_minute
}

///|
pub fn Incident::severity_name(self : Incident) -> String {
  match self.severity {
    Info => "info"
    Warning => "warning"
    Critical => "critical"
  }
}

///|
pub(all) struct IncidentTimeline {
  incidents : Array[Incident]
} derive(Eq, Debug)

///|
pub fn IncidentTimeline::new() -> IncidentTimeline {
  { incidents: [] }
}

///|
pub fn IncidentTimeline::record(
  self : IncidentTimeline,
  incident : Incident,
) -> IncidentTimeline {
  self.incidents.push(incident)
  self
}

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

///|
pub fn IncidentTimeline::active_count(
  self : IncidentTimeline,
  minute : Int,
) -> Int {
  let mut count = 0
  for incident in self.incidents {
    if incident.active_at(minute) {
      count = count + 1
    }
  }
  count
}

///|
pub fn IncidentTimeline::active_at(
  self : IncidentTimeline,
  minute : Int,
) -> Bool {
  self.active_count(minute) > 0
}

///|
pub fn IncidentTimeline::impact_at(
  self : IncidentTimeline,
  minute : Int,
) -> Int {
  let mut impact = 0
  for incident in self.incidents {
    if incident.active_at(minute) && incident.impact_bp > impact {
      impact = incident.impact_bp
    }
  }
  impact
}

///|
pub fn IncidentTimeline::severity_at(
  self : IncidentTimeline,
  minute : Int,
) -> IncidentSeverity {
  let mut severity = Info
  for incident in self.incidents {
    if incident.active_at(minute) {
      match incident.severity {
        Critical => return Critical
        Warning => if severity == Info { severity = Warning }
        Info => ()
      }
    }
  }
  severity
}

///|
pub fn IncidentTimeline::overlap_count(
  self : IncidentTimeline,
  start_minute : Int,
  end_minute : Int,
) -> Int {
  let mut count = 0
  for incident in self.incidents {
    if incident.ended_minute >= start_minute &&
      incident.started_minute <= end_minute {
      count = count + 1
    }
  }
  count
}

///|
pub fn IncidentTimeline::impact_average(
  self : IncidentTimeline,
  start_minute : Int,
  end_minute : Int,
) -> Int {
  let mut total = 0
  let mut count = 0
  for minute in start_minute..<=end_minute {
    let impact = self.impact_at(minute)
    total = total + impact
    count = count + 1
  }
  if count == 0 {
    0
  } else {
    total / count
  }
}

///|
pub fn IncidentTimeline::to_json(self : IncidentTimeline) -> String {
  let result = StringBuilder()
  result.write_string("[")
  for i, incident in self.incidents {
    if i > 0 {
      result.write_string(",")
    }
    result.write_string(
      "{\"id\":\"\{escape_json(incident.id)}\",\"start\":\{incident.started_minute},\"end\":\{incident.ended_minute},\"severity\":\"\{incident.severity_name()}\",\"title\":\"\{escape_json(incident.title)}\",\"impact_bp\":\{incident.impact_bp}}",
    )
  }
  result.write_string("]")
  result.to_string()
}