import std/[httpclient, json, logging, strutils, uri]

const apiBibleRoot* = "https://rest.api.bible/v1"

proc configBibleIdKey(translation: string): string =
  let normalizedTranslation = translation.toLowerAscii
  "apiBible" & normalizedTranslation[0].toUpperAscii &
    normalizedTranslation[1..^1] & "BibleId"

proc defaultBibleId*(translation: string): string =
  case translation.toLowerAscii
  of "niv": "78a9f6124f344018-01"
  else: ""

proc apiGet(apiRoot, path, query, apiKey: string): JsonNode =
  var root = apiRoot
  while root.endsWith("/"): root.setLen(root.len - 1)

  var urlPath = root & path
  if query.len > 0:
    urlPath &= "?" & query

  debug "requesting " & urlPath

  let http = newHttpClient()
  http.headers = newHttpHeaders({"api-key": apiKey})
  parseJson(http.getContent(urlPath))

proc resolveBibleId(translation, apiKey, apiRoot, configuredBibleId: string): string =
  let normalizedTranslation = translation.toLowerAscii

  if configuredBibleId.strip.len > 0:
    return configuredBibleId.strip

  let defaultId = defaultBibleId(normalizedTranslation)
  if defaultId.len > 0:
    return defaultId

  let translationCode = normalizedTranslation.toUpperAscii
  let respJson = apiGet(
    apiRoot,
    "/bibles",
    "language=eng&abbreviation=" & encodeUrl(translationCode) &
      "&include-full-details=false",
    apiKey)

  for bible in respJson["data"].getElems:
    let abbreviation =
      if bible.hasKey("abbreviation"): bible["abbreviation"].getStr else: ""
    let abbreviationLocal =
      if bible.hasKey("abbreviationLocal"): bible["abbreviationLocal"].getStr else: ""

    if abbreviation.toLowerAscii == normalizedTranslation or
       abbreviationLocal.toLowerAscii == normalizedTranslation:
      return bible["id"].getStr

  if respJson["data"].getElems.len > 0:
    return respJson["data"].getElems[0]["id"].getStr

  raise newException(ValueError,
    "could not find an API.Bible Bible ID for '" & translation &
      "'; configure " & configBibleIdKey(normalizedTranslation))

proc resolvePassageId(reference, bibleId, apiKey, apiRoot: string): string =
  let respJson = apiGet(
    apiRoot,
    "/bibles/" & encodeUrl(bibleId) & "/search",
    "query=" & encodeUrl(reference) & "&limit=1&sort=canonical",
    apiKey)

  if respJson["data"].hasKey("passages"):
    let passages = respJson["data"]["passages"].getElems
    if passages.len > 0:
      return passages[0]["id"].getStr

  if respJson["data"].hasKey("verses"):
    let verses = respJson["data"]["verses"].getElems
    if verses.len == 1:
      return verses[0]["id"].getStr

  raise newException(ValueError,
    "could not resolve passage reference '" & reference & "' using API.Bible")

proc fetchPassages*(
    reference,
    translation,
    apiKey,
    apiRoot,
    configuredBibleId: string): seq[string] =

  let bibleId = resolveBibleId(translation, apiKey, apiRoot, configuredBibleId)
  let passageId = resolvePassageId(reference, bibleId, apiKey, apiRoot)

  let respJson = apiGet(
    apiRoot,
    "/bibles/" & encodeUrl(bibleId) & "/passages/" & encodeUrl(passageId),
    "content-type=text&include-notes=false&include-titles=true" &
      "&include-chapter-numbers=false&include-verse-numbers=true" &
      "&include-verse-spans=false",
    apiKey)

  let passage = respJson["data"]
  @[passage["reference"].getStr & "\n" & passage["content"].getStr]
