///|
pub(all) struct ServiceSnapshot {
  service : String
  budget : BudgetReport
} derive(Eq, Debug)

///|
pub fn ServiceSnapshot::new(
  service : String,
  budget : BudgetReport,
) -> ServiceSnapshot {
  { service, budget }
}

///|
pub fn ServiceSnapshot::healthy(self : ServiceSnapshot) -> Bool {
  self.budget.healthy()
}

///|
pub fn ServiceSnapshot::availability_bp(self : ServiceSnapshot) -> Int {
  self.budget.window.availability_bp()
}

///|
pub(all) struct PortfolioSummary {
  portfolio : String
  service_count : Int
  healthy_count : Int
  unhealthy_count : Int
  total_requests : Int
  total_bad : Int
  availability_bp : Int
} derive(Eq, Debug)

///|
pub fn PortfolioSummary::healthy(self : PortfolioSummary) -> Bool {
  self.unhealthy_count == 0 && self.service_count > 0
}

///|
pub fn PortfolioSummary::to_json(self : PortfolioSummary) -> String {
  "{\"portfolio\":\"\{escape_json(self.portfolio)}\",\"service_count\":\{self.service_count},\"healthy_count\":\{self.healthy_count},\"unhealthy_count\":\{self.unhealthy_count},\"total_requests\":\{self.total_requests},\"total_bad\":\{self.total_bad},\"availability_bp\":\{self.availability_bp}}"
}

///|
pub(all) struct SloPortfolio {
  name : String
  services : Array[ServiceSnapshot]
} derive(Eq, Debug)

///|
pub fn SloPortfolio::new(name : String) -> SloPortfolio {
  { name, services: [] }
}

///|
pub fn SloPortfolio::add(
  self : SloPortfolio,
  service : ServiceSnapshot,
) -> SloPortfolio {
  self.services.push(service)
  self
}

///|
pub fn SloPortfolio::service_count(self : SloPortfolio) -> Int {
  self.services.length()
}

///|
pub fn SloPortfolio::contains(self : SloPortfolio, service : String) -> Bool {
  for item in self.services {
    if item.service == service {
      return true
    }
  }
  false
}

///|
pub fn SloPortfolio::healthy_count(self : SloPortfolio) -> Int {
  let mut count = 0
  for service in self.services {
    if service.healthy() {
      count = count + 1
    }
  }
  count
}

///|
pub fn SloPortfolio::summary(self : SloPortfolio) -> PortfolioSummary {
  let mut total_requests = 0
  let mut total_bad = 0
  let mut availability_numerator = 0
  for service in self.services {
    total_requests = total_requests + service.budget.window.total
    total_bad = total_bad + service.budget.actual_bad
    availability_numerator = availability_numerator +
      service.budget.window.good * 10000
  }
  let count = self.services.length()
  let availability = if total_requests == 0 {
    10000
  } else {
    availability_numerator / total_requests
  }
  {
    portfolio: self.name,
    service_count: count,
    healthy_count: self.healthy_count(),
    unhealthy_count: count - self.healthy_count(),
    total_requests,
    total_bad,
    availability_bp: availability,
  }
}

///|
pub fn SloPortfolio::unhealthy_services(self : SloPortfolio) -> Array[String] {
  let result = []
  for service in self.services {
    if !service.healthy() {
      result.push(service.service)
    }
  }
  result
}

///|
pub fn SloPortfolio::to_json(self : SloPortfolio) -> String {
  let result = StringBuilder()
  result.write_string("{\"name\":\"\{escape_json(self.name)}\",\"services\":[")
  for i, service in self.services {
    if i > 0 {
      result.write_string(",")
    }
    result.write_string(
      "{\"name\":\"\{escape_json(service.service)}\",\"healthy\":\{service.healthy()},\"budget\":\{service.budget.to_json()}}",
    )
  }
  result.write_string("]}")
  result.to_string()
}