///|
/// NCBI BLAST web service demo (port of Biopython Bio.Blast.NCBIWWW.qblast).
///
/// The client takes an injected transport, so this demo runs fully offline
/// against a mock server that speaks the same protocol as the real NCBI
/// endpoint: PUT returns an RID + RTOE, SearchInfo polls return WAITING
/// then READY, and the final Get returns the result document. In real use
/// the fetch callback would perform an HTTP GET.

///|
/// A tiny stateful mock server: one WAITING poll, then READY, then the
/// "result" document.
struct MockBlast {
  mut polls : Int32
  requests : Array[String]
}

///|
fn main {
  let mock : MockBlast = { polls: 0, requests: [], }

  let client = @src.NcbiBlastClient::new(now=fn() { 0.0 }, fetch=fn(url) {
    mock.requests.push(url)
    if url.contains("CMD=Put") {
      // Real NCBI answers with an RID and an estimated wait (RTOE).
      "  RID = 9ZZKQ2A014  \n  RTOE = 15\n"
    } else if url.contains("FORMAT_OBJECT=SearchInfo") {
      let n = mock.polls
      mock.polls = n + 1
      if n < 1 {
        "Status=WAITING\n"
      } else {
        "Status=READY\n"
      }
    } else {
      "\n\n\n" +
      "  \n    ... mock hits ...\n" +
      "  \n\n"
    }
  })

  // Inspect the URL Biopython would build for a blastn nr search.
  let url = @src.qblast_put_url(
    "blastn",
    "nr",
    "ACGTACGTACGTACGT",
    megablast=false,
    hitlist_size=100,
  ) catch {
    @src.NcbiBlastError(msg) => {
      println("URL build failed: " + msg)
      return
    }
  }
  println("PUT url:")
  println(url)
  println("")

  // Run the whole submit -> poll -> fetch workflow.
  let result = client.qblast(
    "blastn",
    "nr",
    "ACGTACGTACGTACGT",
    hitlist_size=100,
  ) catch {
    @src.NcbiBlastError(msg) => {
      println("qblast failed: " + msg)
      return
    }
  }

  println("requests issued: " + mock.requests.length().to_string())
  let mut i = 0
  while i < mock.requests.length() {
    let u = mock.requests[i]
    let kind = if u.contains("CMD=Put") {
      "submit"
    } else if u.contains("SearchInfo") {
      "poll  "
    } else {
      "result"
    }
    let shown = if u.length() > 96 { u[0:96].to_owned() } else { u }
    println("  " + kind + " : " + shown)
    i = i + 1
  }
  println("")
  println("result document:")
  println(result)
}