///|
pub(all) enum ZoneDirective {
  OriginDirective(DomainName)
  DefaultTtlDirective(Ttl)
} derive(Eq, Debug)

///|
fn directive_error(
  code : ZoneErrorCode,
  message : String,
  token : ZoneToken,
) -> ZoneError {
  ZoneError::new(code, message, token.span())
}

///|
fn require_directive_arity(
  tokens : Array[ZoneToken],
  expected : Int,
) -> Result[Unit, ZoneError] {
  if tokens.length() == expected {
    Ok(())
  } else {
    Err(
      directive_error(
        InvalidDirective,
        "zone directive has an invalid number of fields",
        tokens[0],
      ),
    )
  }
}

///|
fn parse_origin_directive(
  tokens : Array[ZoneToken],
  current_origin : DomainName?,
) -> Result[ZoneDirective, ZoneError] {
  match require_directive_arity(tokens, 2) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let name = match parse_domain_name(tokens[1].text()) {
    Ok(value) => value
    Err(error) =>
      return Err(
        ZoneError::new(InvalidDomainName, error.message(), tokens[1].span()),
      )
  }
  if name.is_absolute() {
    Ok(OriginDirective(name))
  } else {
    match current_origin {
      Some(origin) =>
        match name.resolve(origin) {
          Ok(value) => Ok(OriginDirective(value))
          Err(error) => Err(error)
        }
      None =>
        Err(
          directive_error(
            RelativeNameWithoutOrigin,
            "relative $ORIGIN requires an existing absolute origin",
            tokens[1],
          ),
        )
    }
  }
}

///|
fn parse_ttl_directive(
  tokens : Array[ZoneToken],
) -> Result[ZoneDirective, ZoneError] {
  match require_directive_arity(tokens, 2) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  match parse_ttl(tokens[1].text()) {
    Ok(value) => Ok(DefaultTtlDirective(value))
    Err(error) =>
      Err(ZoneError::new(InvalidTtl, error.message(), tokens[1].span()))
  }
}

///|
pub fn parse_zone_directive(
  statement : ZoneStatement,
  current_origin? : DomainName? = None,
) -> Result[ZoneDirective, ZoneError] {
  let tokens = statement.tokens()
  if tokens.length() == 0 {
    return Err(
      ZoneError::new(
        InvalidDirective,
        "zone directive statement is empty",
        SourceSpan::point(0, 0),
      ),
    )
  }
  let keyword = ascii_lower(tokens[0].text())
  match keyword {
    "$origin" => parse_origin_directive(tokens, current_origin)
    "$ttl" => parse_ttl_directive(tokens)
    "$include" =>
      Err(
        directive_error(
          UnsupportedDirective,
          "$INCLUDE requires caller-managed file I/O and is not loaded",
          tokens[0],
        ),
      )
    _ =>
      Err(
        directive_error(
          InvalidDirective,
          "statement does not begin with a supported zone directive",
          tokens[0],
        ),
      )
  }
}