// Record types and mandatory field helpers (ISO 28500:2017 clauses
// 5.2, 5.4, 5.5, 6).
//
// The eight standard record types are modelled as an enum so callers
// can match on them; parsing is case-insensitive per the field-value
// conventions of the specification, and unknown types map to `None`
// (readers must skip records of unknown type rather than fail).

///|
/// The eight standard WARC record types of clause 6.
pub enum WarcRecordType {
  Warcinfo
  Response
  Resource
  Request
  Metadata
  Revisit
  Conversion
  Continuation
} derive(Eq, @debug.Debug)

///|
/// Every record type code in declaration order (lowercase, as written
/// in the specification).
pub fn all_record_type_codes() -> Array[String] {
  [
    "warcinfo", "response", "resource", "request", "metadata", "revisit", "conversion",
    "continuation",
  ]
}

///|
/// Resolve a lowercase type code back to a record type.
pub fn record_type_of_code(code : String) -> WarcRecordType? {
  match code {
    "warcinfo" => Some(Warcinfo)
    "response" => Some(Response)
    "resource" => Some(Resource)
    "request" => Some(Request)
    "metadata" => Some(Metadata)
    "revisit" => Some(Revisit)
    "conversion" => Some(Conversion)
    "continuation" => Some(Continuation)
    _ => None
  }
}

///|
/// Parse a WARC-Type field value case-insensitively; unknown types
/// yield `None`.
pub fn WarcRecordType::parse(s : String) -> WarcRecordType? {
  record_type_of_code(s.to_lower())
}

///|
/// The canonical lowercase spelling of the type.
pub fn WarcRecordType::type_name(self : WarcRecordType) -> String {
  match self {
    Warcinfo => "warcinfo"
    Response => "response"
    Resource => "resource"
    Request => "request"
    Metadata => "metadata"
    Revisit => "revisit"
    Conversion => "conversion"
    Continuation => "continuation"
  }
}

///|
/// The record type declared by WARC-Type, or `None` when the field is
/// absent or carries an unknown type.
pub fn WarcRecord::record_type(self : WarcRecord) -> WarcRecordType? {
  let v = self.field_first("WARC-Type")
  match v {
    Some(t) => WarcRecordType::parse(t)
    None => None
  }
}

///|
/// The value of a mandatory field, or a structured error naming it.
pub fn WarcRecord::require_field(
  self : WarcRecord,
  name : String,
  record_index : Int64,
) -> Result[String, WarcError] {
  let v = self.field_first(name)
  match v {
    Some(x) => Ok(x)
    None =>
      Err(
        WarcError::new(
          WarcErrorStage::Record,
          WarcErrorKind::MissingRequiredField,
          0L,
          record_index,
          "missing mandatory field \{name}",
        ),
      )
  }
}

///|
/// The WARC-Record-ID of the record: the mandatory field, parsed as a
/// `` reference. Returns the URI inside the angle brackets.
pub fn WarcRecord::record_id(
  self : WarcRecord,
  record_index : Int64,
) -> Result[String, WarcError] {
  let v = self.require_field("WARC-Record-ID", record_index)
  let value = match v {
    Ok(x) => x
    Err(e) => return Err(e)
  }
  parse_uri_ref(value, record_index)
}

///|
/// The WARC-Date of the record, parsed to a structured date.
pub fn WarcRecord::warc_date(
  self : WarcRecord,
  record_index : Int64,
) -> Result[WarcDate, WarcError] {
  let v = self.require_field("WARC-Date", record_index)
  let value = match v {
    Ok(x) => x
    Err(e) => return Err(e)
  }
  parse_warc_date(value, record_index)
}

///|
/// The WARC-Target-URI of the record, parsed as a `` reference.
/// Callers decide whether the record type requires the field; this
/// helper parses it whenever it is present.
pub fn WarcRecord::target_uri(
  self : WarcRecord,
  record_index : Int64,
) -> Result[String, WarcError] {
  let v = self.require_field("WARC-Target-URI", record_index)
  let value = match v {
    Ok(x) => x
    Err(e) => return Err(e)
  }
  parse_uri_ref(value, record_index)
}

///|
/// The raw Content-Length field value, when present.
pub fn WarcRecord::declared_content_length(self : WarcRecord) -> String? {
  self.field_first("Content-Length")
}