///|
pub enum CsvParseError {
  EmptyCsv
  UnterminatedQuote
  UnexpectedCharacter
  InvalidHeader
  InvalidNumber
} derive(Debug, Eq)

///|
pub struct ReportSummaryRow {
  title : String
  input_count : Int
  result_count : Int
  warning_count : Int
  source_count : Int
  publishable : Bool
} derive(Debug, Eq)

///|
fn csv_rows(text : String) -> Result[Array[Array[String]], CsvParseError] {
  if text == "" {
    return Err(EmptyCsv)
  }
  let rows : Array[Array[String]] = []
  let fields : Array[String] = []
  let mut field = ""
  let mut in_quotes = false
  let mut quote_pending = false
  for c in text {
    if quote_pending {
      if c == '"' {
        field += "\""
        quote_pending = false
        in_quotes = true
      } else {
        in_quotes = false
        quote_pending = false
        if c == ',' {
          fields.push(field)
          field = ""
        } else if c == '\n' {
          fields.push(field)
          rows.push(fields.copy())
          fields.clear()
          field = ""
        } else if c == '\r' {
          ()
        } else {
          return Err(UnexpectedCharacter)
        }
      }
    } else if in_quotes {
      if c == '"' {
        quote_pending = true
      } else {
        field += "\{c}"
      }
    } else if c == '"' && field == "" {
      in_quotes = true
    } else if c == ',' {
      fields.push(field)
      field = ""
    } else if c == '\n' {
      fields.push(field)
      rows.push(fields.copy())
      fields.clear()
      field = ""
    } else if c == '\r' {
      ()
    } else {
      field += "\{c}"
    }
  }
  if quote_pending {
    in_quotes = false
  }
  if in_quotes {
    return Err(UnterminatedQuote)
  }
  fields.push(field)
  if fields.length() > 1 || fields[0] != "" {
    rows.push(fields)
  }
  Ok(rows)
}

///|
fn csv_count(text : String) -> Int? {
  match decimal_scaled(text) {
    Some(value) => if value % 1000 == 0 { Some(value / 1000) } else { None }
    None => None
  }
}

///|
pub fn parse_report_summary_csv(
  text : String,
) -> Result[Array[ReportSummaryRow], CsvParseError] {
  let rows = match csv_rows(text) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if rows.length() == 0 ||
    rows[0] !=
    [
      "title", "inputs", "results", "warnings", "sources", "publishable", "first_input_note",
    ] {
    return Err(InvalidHeader)
  }
  let output : Array[ReportSummaryRow] = []
  for row in rows[1:] {
    if row.length() < 6 {
      return Err(InvalidHeader)
    }
    let input_count = csv_count(row[1])
    let result_count = csv_count(row[2])
    let warning_count = csv_count(row[3])
    let source_count = csv_count(row[4])
    if input_count is None ||
      result_count is None ||
      warning_count is None ||
      source_count is None {
      return Err(InvalidNumber)
    }
    if row[5] != "true" && row[5] != "false" {
      return Err(InvalidHeader)
    }
    output.push({
      title: row[0],
      input_count: input_count.unwrap(),
      result_count: result_count.unwrap(),
      warning_count: warning_count.unwrap(),
      source_count: source_count.unwrap(),
      publishable: row[5] == "true",
    })
  }
  Ok(output)
}