///|
/// Which model answers the question. `Exa` is the default; `ExaFast` trades
/// depth for latency, `ExaPro` and `ExaResearch` go the other way.
pub(all) enum AnswerModel {
  Exa
  ExaPro
  ExaResearch
  ExaFast
}

///|
pub impl ToJson for AnswerModel with fn to_json(self) {
  match self {
    Exa => "exa".to_json()
    ExaPro => "exa-pro".to_json()
    ExaResearch => "exa-research".to_json()
    ExaFast => "exa-fast".to_json()
  }
}

///|
/// The result of an `/answer` call.
pub(all) struct AnswerResponse {
  request_id : String?
  /// A string, unless the request supplied an `output_schema`, in which case an
  /// object matching it. Use `answer_text` for the string case.
  answer : Json
  answer_text : String?
  /// The pages the answer was drawn from.
  citations : Array[SearchResult]
  cost_dollars : CostDollars?
  raw : Json
} derive(Eq, @debug.Debug)

///|
/// Build a response directly, for testing code that consumes one.
///
/// `answer_text` is filled in from `answer` when it is a plain string, the same
/// way decoding does it.
pub fn AnswerResponse::new(
  answer? : Json = Json::null(),
  citations? : Array[SearchResult] = [],
  request_id? : String,
  cost_dollars? : CostDollars,
  raw? : Json = Json::null(),
) -> AnswerResponse {
  let answer_text = if answer is String(text) { Some(text) } else { None }
  { request_id, answer, answer_text, citations, cost_dollars, raw, }
}

///|
fn AnswerResponse::decode(json : Json) -> AnswerResponse raise ExaError {
  let citations = []
  for item in get_array(json, "citations") {
    citations.push(SearchResult::decode(item, "answer citation"))
  }
  {
    request_id: get_str(json, "requestId"),
    answer: field(json, "answer").unwrap_or(Json::null()),
    answer_text: get_str(json, "answer"),
    citations,
    cost_dollars: field(json, "costDollars").map(CostDollars::decode),
    raw: json,
  }
}

///|
/// Ask a question and get an answer with citations.
///
/// Exa runs the search and the synthesis; set `text` to also get each cited
/// page's full text back, and `output_schema` to get a structured answer
/// instead of prose.
pub async fn Client::answer(
  self : Client,
  query : String,
  model? : AnswerModel,
  text? : Bool,
  system_prompt? : String,
  user_location? : String,
  output_schema? : Json,
) -> AnswerResponse {
  let body = JsonObject::new()
  body.set("query", query.to_json())
  body.set_opt("model", model)
  body.set_opt("text", text)
  body.set_opt("systemPrompt", system_prompt)
  body.set_opt("userLocation", user_location)
  body.set_opt("outputSchema", output_schema)
  AnswerResponse::decode(self.post_json("/answer", body.build()))
}