///|
pub(all) enum IgnoredErrorType {
  EvalError
  TwoDigitTextYear
  NumberStoredAsText
  Formula
  FormulaRange
  UnlockedFormula
  EmptyCellReference
  ListDataValidation
  CalculatedColumn
} derive(Eq, Debug)

///|
pub struct IgnoredError {
  sqref : String
  eval_error : Bool
  two_digit_text_year : Bool
  number_stored_as_text : Bool
  formula : Bool
  formula_range : Bool
  unlocked_formula : Bool
  empty_cell_reference : Bool
  list_data_validation : Bool
  calculated_column : Bool
} derive(Eq, Debug)

///|
fn ignored_error_from_type(
  sqref : String,
  error_type : IgnoredErrorType,
) -> IgnoredError {
  let eval_error = error_type == EvalError
  let two_digit_text_year = error_type == TwoDigitTextYear
  let number_stored_as_text = error_type == NumberStoredAsText
  let formula = error_type == Formula
  let formula_range = error_type == FormulaRange
  let unlocked_formula = error_type == UnlockedFormula
  let empty_cell_reference = error_type == EmptyCellReference
  let list_data_validation = error_type == ListDataValidation
  let calculated_column = error_type == CalculatedColumn
  {
    sqref,
    eval_error,
    two_digit_text_year,
    number_stored_as_text,
    formula,
    formula_range,
    unlocked_formula,
    empty_cell_reference,
    list_data_validation,
    calculated_column,
  }
}

///|
fn write_ignored_error_xml(
  sb : LimitedXmlBuilder,
  error : IgnoredError,
) -> Unit raise XlsxError {
  if error.sqref == "" {
    raise InvalidSheetOperation(msg="ignored errors sqref empty")
  }
  sb.write_view("")
}

///|
fn ignored_error_xml(error : IgnoredError) -> String raise XlsxError {
  let sb = LimitedXmlBuilder::new(None)
  write_ignored_error_xml(sb, error)
  sb.to_string()
}

///|
fn write_ignored_errors_xml(
  errors : ArrayView[IgnoredError],
  budget? : WritePartBudget,
) -> String? raise XlsxError {
  if errors.length() == 0 {
    return None
  }
  let sb = LimitedXmlBuilder::new(budget)
  sb.write_view("  \n")
  for error in errors {
    sb.write_view("    ")
    write_ignored_error_xml(sb, error)
    sb.write_view("\n")
  }
  sb.write_view("  \n")
  Some(sb.to_string())
}

///|
fn parse_ignored_errors(
  xml : StringView,
  budget? : ReadBudget,
) -> Array[IgnoredError] raise XlsxError {
  let body = match extract_tag_body(xml, "ignoredErrors") {
    Some(value) => value
    None => return []
  }
  let out : Array[IgnoredError] = []
  let mut first = true
  for chunk in body.split("") {
      Some(pos) => pos
      None =>
        match text.find(">") {
          Some(pos) => pos
          None => raise InvalidXml(msg="ignoredError tag not closed")
        }
    }
    let tag = text[:end]
    let sqref = match attr_value(tag, "sqref") {
      Some(value) =>
        normalize_sqref_for_read(
          unescape_xml_text(value),
          "ignoredError",
          budget?,
        )
      None => raise InvalidXml(msg="ignoredError sqref missing")
    }
    let error : IgnoredError = {
      sqref,
      eval_error: match attr_value(tag, "evalError") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      two_digit_text_year: match attr_value(tag, "twoDigitTextYear") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      number_stored_as_text: match attr_value(tag, "numberStoredAsText") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      formula: match attr_value(tag, "formula") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      formula_range: match attr_value(tag, "formulaRange") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      unlocked_formula: match attr_value(tag, "unlockedFormula") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      empty_cell_reference: match attr_value(tag, "emptyCellReference") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      list_data_validation: match attr_value(tag, "listDataValidation") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
      calculated_column: match attr_value(tag, "calculatedColumn") {
        Some(value) => parse_bool_attr(value)
        None => false
      },
    }
    out.push(error)
  }
  out
}

///|
test "ignored errors wb: ignored_error_xml rejects empty sqref" {
  let bad : Result[String, Error] = Ok(
    ignored_error_xml({
      sqref: "",
      eval_error: false,
      two_digit_text_year: false,
      number_stored_as_text: false,
      formula: false,
      formula_range: false,
      unlocked_formula: false,
      empty_cell_reference: false,
      list_data_validation: false,
      calculated_column: false,
    }),
  ) catch {
    e => Err(e)
  }
  inspect(bad is Err(XlsxError::InvalidSheetOperation(_)), content="true")
}

///|
test "ignored errors wb: parse non-self-closing ignoredError tag" {
  let parsed = parse_ignored_errors(
    "",
  ) catch {
    err => fail("parse_ignored_errors failed: \{repr(err)}")
  }
  inspect(parsed.length(), content="1")
  inspect(parsed[0].sqref, content="A1")
}

///|
test "ignored errors wb: parse requires sqref attribute" {
  let missing_sqref : Result[Array[IgnoredError], Error] = Ok(
    parse_ignored_errors(
      "",
    ),
  ) catch {
    e => Err(e)
  }
  inspect(missing_sqref is Err(XlsxError::InvalidXml(_)), content="true")
}