lib/librarian.ex

defmodule BibleEx.Librarian do
  @moduledoc """
  Internal helper functions for looking up Bible metadata and validating references.

  This module is used by `BibleEx.Reference` and `BibleEx.RefParser` to:

    * Resolve book names into canonical numbers and name variants.
    * Compute last chapters and verses for a given book.
    * Build lists of `BibleEx.Chapter` and `BibleEx.Verse` structs.
    * Infer reference types and validate that references are in range.
    * Render human-readable reference strings.
  """

  require Logger
  alias BibleEx.BibleData

  @doc ~S"""
  Returns the book number (1–66) for a given book identifier.

  The `book` argument may be:

    * The full lowercased book name, e.g. `"genesis"`.
    * An OSIS identifier, e.g. `"gen"`.
    * A shortened/abbreviated key from `BibleEx.BibleData`.
    * A variant key, e.g. `"mat"` for Matthew.

  If the book cannot be resolved, `nil` is returned.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.find_book_number(book: "Genesis")
      1

      iex> Librarian.find_book_number(book: "rom")
      45

      iex> Librarian.find_book_number(book: "NotABook")
      nil

  """
  def find_book_number(book: book) do
    book_lower = String.downcase(book)

    cond do
      book_lower == "" ->
        nil

      BibleData.books()[book_lower] ->
        BibleData.books()[book_lower]

      BibleData.osis_books()[book_lower] ->
        BibleData.osis_books()[book_lower]

      BibleData.shortened_books()[book_lower] ->
        BibleData.shortened_books()[book_lower]

      BibleData.variants()[book_lower] ->
        BibleData.variants()[book_lower]

      true ->
        nil
    end
  end

  @doc ~S"""
  Returns `true` if the given book string is a recognized Bible book.

  This check uses the canonical, OSIS, and shortened maps in `BibleEx.BibleData`
  and does not attempt to correct misspellings.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.check_book(book: "Genesis")
      true

      iex> Librarian.check_book(book: "Gen")
      true

      iex> Librarian.check_book(book: "NotABook")
      false

  """
  def check_book(book: book) do
    book_lower = String.downcase(book)

    Map.has_key?(BibleData.books(), book_lower) ||
      Map.has_key?(BibleData.osis_books(), book_lower) ||
      Map.has_key?(BibleData.shortened_books(), book_lower)
  end

  @doc ~S"""
  Returns all name variants for a book as a map.

  The result map contains:

    * `:osis` – OSIS identifier (e.g. `"Gen"`)
    * `:abbr` – Paratext-style abbreviation (e.g. `"GEN"`)
    * `:name` – Full book name (e.g. `"Genesis"`)
    * `:short` – Shortened form (e.g. `"Gn"`)

  The `book` argument may be a name/abbreviation string or a 1-based book number.
  If the book cannot be resolved, an empty map is returned.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.get_book_names(book: "Genesis")[:name]
      "Genesis"

      iex> Librarian.get_book_names(book: 45)[:osis]
      "Rom"

      iex> Librarian.get_book_names(book: "NotABook")
      %{}

  """
  def get_book_names(book: book) do
    found_book =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
        "nil"    -> nil
        "atom"   -> nil
      end

    case found_book do
      nil ->
        %{}

      found_book ->
        list = Enum.at(BibleData.book_names(), found_book - 1)

        %{
          osis: Enum.at(list, 0),
          abbr: Enum.at(list, 1),
          name: Enum.at(list, 2),
          short: Enum.at(list, 3)
        }
    end
  end

  @doc ~S"""
  Returns the last verse number for a given book, or for a specific chapter.

  When only `book` is given, the last verse in the final chapter is returned.
  When `chapter` is given, the last verse in that chapter is returned, or `nil`
  if the chapter is out of range.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.get_last_verse_number(book: "Genesis", chapter: 1) > 0
      true

      iex> Librarian.get_last_verse_number(book: "Revelation")
      v when is_integer(v) and v > 0

      iex> Librarian.get_last_verse_number(book: "Genesis", chapter: 999)
      nil

  """
  def get_last_verse_number(book: book) do
    get_last_verse_number(book: book, chapter: nil)
  end

  def get_last_verse_number(book: book, chapter: chapter) do
    found_book =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
      end

    case found_book do
      nil ->
        nil

      _found_book_not_nil ->
        found_chapter =
          case chapter do
            nil   -> length(Enum.at(BibleData.last_verse(), found_book - 1))
            found -> found
          end

        if length(Enum.at(BibleData.last_verse(), found_book - 1)) < found_chapter ||
             found_chapter < 1 do
          nil
        else
          verses_in_book = Enum.at(BibleData.last_verse(), found_book - 1)
          Enum.at(verses_in_book, found_chapter - 1)
        end
    end
  end

  @doc ~S"""
  Returns the number of chapters in a book.

  The `book` argument may be a name/abbreviation string or a book number.
  If the book is not recognized, `nil` is returned.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.get_last_chapter_number(book: "Genesis")
      50

      iex> Librarian.get_last_chapter_number(book: "NotABook")
      nil

  """
  def get_last_chapter_number(book: book) do
    found_book =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
      end

    if found_book > BibleData.last_verse() do
      nil
    else
      length(Enum.at(BibleData.last_verse(), found_book - 1))
    end
  end

  @doc ~S"""
  Builds a `BibleEx.Verse` struct for the last verse of a book or chapter.

  When only `book` is given, the verse is the last verse of the last chapter.
  When `chapter` is given, the verse is the last verse of that chapter.

  Returns `nil` if the book or chapter cannot be resolved.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> last = Librarian.get_last_verse(book: "Jude")
      iex> {last.chapter_number, last.verse_number}
      {1, v} when v > 0

      iex> Librarian.get_last_verse(book: "NotABook")
      nil

  """
  def get_last_verse(book: book) do
    get_last_verse(book: book, chapter: nil)
  end

  def get_last_verse(book: book, chapter: chapter) do
    book_number =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
      end

    book_chapter =
      case chapter do
        nil     -> length(Enum.at(BibleData.last_verse(), book_number - 1))
        chapter -> chapter
      end

    book_names = get_book_names(book: book)

    if is_nil(book_number) do
      nil
    else
      case Map.get(book_names, :name, nil) do
        nil ->
          nil

        book_name ->
          BibleEx.Verse.new(
            book: book_name,
            chapter_number: book_chapter,
            verse_number: get_last_verse_number(book: book_number, chapter: book_chapter)
          )
      end
    end
  end

  @doc ~S"""
  Returns all verses in a chapter as a list of `BibleEx.Verse` structs.

  When only `book` and `chapter` are given, the entire chapter is returned.
  When `start_verse` and/or `end_verse` are given, a subrange of verses is returned.

  Returns `nil` if the book or chapter is invalid, or if the requested range
  is out of bounds (e.g. `start_verse > end_verse`).

  ## Examples

      iex> alias BibleEx.Librarian
      iex> verses = Librarian.get_verses(book: "Genesis", chapter: 1)
      iex> hd(verses).reference
      "Genesis 1:1"

      iex> range = Librarian.get_verses(book: "Genesis", chapter: 1, start_verse: 1, end_verse: 3)
      iex> Enum.map(range, & &1.verse_number)
      [1, 2, 3]

  """
  def get_verses(book: book, chapter: chapter) do
    get_verses(book: book, chapter: chapter, start_verse: nil, end_verse: nil)
  end

  def get_verses(book: book, chapter: chapter, start_verse: start_verse, end_verse: end_verse) do
    book_number =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
      end

    if is_nil(book_number) do
      nil
    else
      case chapter do
        nil ->
          nil

        chapter ->
          start_verse =
            if !is_nil(start_verse), do: start_verse, else: 1

          end_verse =
            if !is_nil(end_verse) do
              end_verse
            else
              BibleData.last_verse()
              |> Enum.at(book_number - 1)
              |> Enum.at(chapter - 1)
            end

          case start_verse > end_verse do
            true ->
              nil

            false ->
              case end_verse do
                nil ->
                  nil

                last_verse ->
                  book_names = get_book_names(book: book)

                  case Map.get(book_names, :name, nil) do
                    nil ->
                      nil

                    book_name ->
                      Enum.map(start_verse..last_verse, fn x ->
                        BibleEx.Verse.new(
                          book: book_name,
                          chapter_number: chapter,
                          verse_number: x
                        )
                      end)
                  end
              end
          end
      end
    end
  end

  @doc ~S"""
  Returns a `BibleEx.Chapter` for the last chapter of a book.

  Returns `nil` if the book cannot be resolved.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> ch = Librarian.get_last_chapter(book: "Psalms")
      iex> ch.chapter_number
      150

  """
  def get_last_chapter(book: book) do
    found_book =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
      end

    if found_book > BibleData.last_verse() do
      nil
    else
      book_names = get_book_names(book: book)

      case Map.get(book_names, :name, nil) do
        nil ->
          nil

        book_name ->
          last_chapter = length(Enum.at(BibleData.last_verse(), found_book - 1))
          BibleEx.Chapter.new(book: book_name, chapter_number: last_chapter)
      end
    end
  end

  @doc ~S"""
  Returns a list of `BibleEx.Chapter` structs for a book.

  Supported forms:

    * `get_chapters/1` – all chapters in a book.
    * `get_chapters/2` – a single chapter.
    * `get_chapters/3` – a range of chapters.

  Chapters outside the valid range are clamped to the book's first/last chapter.
  Returns `nil` if the book is invalid.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> chs = Librarian.get_chapters(book: "Genesis")
      iex> length(chs)
      50

      iex> [ch] = Librarian.get_chapters(book: "Genesis", start_chapter: 1)
      iex> ch.chapter_number
      1

      iex> range = Librarian.get_chapters(book: "Genesis", start_chapter: 1, end_chapter: 3)
      iex> Enum.map(range, & &1.chapter_number)
      [1, 2, 3]

  """
  def get_chapters(book: book) do
    get_chapters(book: book, start_chapter: nil, end_chapter: nil)
  end

  def get_chapters(book: book, start_chapter: start_chapter) do
    get_chapters(book: book, start_chapter: start_chapter, end_chapter: nil)
  end

  def get_chapters(book: book, start_chapter: start_chapter, end_chapter: end_chapter) do
    found_book =
      case BibleEx.typeof(book) do
        "binary" -> find_book_number(book: book)
        "number" -> book
      end

    if found_book > BibleData.last_verse() do
      nil
    else
      book_names = get_book_names(book: book)

      if is_nil(found_book) do
        nil
      else
        case Map.get(book_names, :name, nil) do
          nil ->
            nil

          book_name ->
            case start_chapter > end_chapter do
              true ->
                nil

              false ->
                start_chapter =
                  if is_nil(start_chapter), do: 1, else: start_chapter

                end_chapter =
                  if is_nil(end_chapter), do: start_chapter, else: end_chapter

                start_chapter =
                  if start_chapter < 1, do: 1, else: start_chapter

                end_chapter =
                  if end_chapter > length(Enum.at(BibleData.last_verse(), found_book - 1)) do
                    length(Enum.at(BibleData.last_verse(), found_book - 1))
                  else
                    end_chapter
                  end

                Enum.map(start_chapter..end_chapter, fn x ->
                  BibleEx.Chapter.new(book: book_name, chapter_number: x)
                end)
            end
        end
      end
    end
  end

  @doc ~S"""
  Infers the type of a reference (`:book`, `:chapter`, `:verse`, `:chapter_range`, `:verse_range`).

  This set of overloads allows calling with increasing specificity:

    * `identify_reference_type/1` – book only.
    * `identify_reference_type/2` – book and start chapter.
    * `identify_reference_type/3` – book, start chapter, start verse.
    * `identify_reference_type/4` – plus end chapter.
    * `identify_reference_type/5` – plus end verse.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.identify_reference_type(book: "Genesis")
      :book

      iex> Librarian.identify_reference_type(book: "Genesis", start_chapter: 1)
      :chapter

      iex> Librarian.identify_reference_type(book: "Genesis", start_chapter: 1, start_verse: 1)
      :verse

      iex> Librarian.identify_reference_type(book: "Genesis", start_chapter: 1, end_chapter: 2)
      :chapter_range

      iex> Librarian.identify_reference_type(book: "Genesis", start_chapter: 1, start_verse: 1, end_chapter: 1, end_verse: 5)
      :verse_range

  """
  def identify_reference_type(book: book) do
    identify_reference_type(
      book: book,
      start_chapter: nil,
      start_verse: nil,
      end_chapter: nil,
      end_verse: nil
    )
  end

  def identify_reference_type(
        book: book,
        start_chapter: start_chapter
      ) do
    identify_reference_type(
      book: book,
      start_chapter: start_chapter,
      start_verse: nil,
      end_chapter: nil,
      end_verse: nil
    )
  end

  def identify_reference_type(
        book: book,
        start_chapter: start_chapter,
        start_verse: start_verse
      ) do
    identify_reference_type(
      book: book,
      start_chapter: start_chapter,
      start_verse: start_verse,
      end_chapter: nil,
      end_verse: nil
    )
  end

  def identify_reference_type(
        book: book,
        start_chapter: start_chapter,
        start_verse: start_verse,
        end_chapter: end_chapter
      ) do
    identify_reference_type(
      book: book,
      start_chapter: start_chapter,
      start_verse: start_verse,
      end_chapter: end_chapter,
      end_verse: nil
    )
  end

  def identify_reference_type(
        book: _book,
        start_chapter: start_chapter,
        start_verse: start_verse,
        end_chapter: end_chapter,
        end_verse: end_verse
      ) do
    cond do
      is_nil(start_chapter) and is_nil(end_chapter) ->
        if !is_nil(start_verse) do
          if !is_nil(end_verse) do
            :verse_range
          else
            :verse
          end
        else
          :book
        end

      !is_nil(start_chapter) and !is_nil(end_chapter) and start_chapter != end_chapter ->
        :chapter_range

      !is_nil(start_chapter) and (is_nil(end_chapter) or end_chapter == start_chapter) ->
        if !is_nil(start_verse) do
          if !is_nil(end_verse) do
            :verse_range
          else
            :verse
          end
        else
          :chapter
        end

      !is_nil(start_verse) and !is_nil(end_verse) ->
        :verse_range

      !is_nil(start_verse) ->
        :verse

      true ->
        nil
    end
  end

  @doc ~S"""
Validates that a book identifier refers to a real Bible book.

The `book` argument may be a name/abbreviation string or a numeric book
index. Only the book is checked; chapters and verses are not considered
in this arity.

## Examples

    iex> alias BibleEx.Librarian
    iex> Librarian.verify_reference(book: "Genesis")
    true

    iex> Librarian.verify_reference(book: "NotABook")
    false

"""
def verify_reference(book: book) when not is_nil(book) do
  verified = true

  found_book =
    case BibleEx.typeof(book) do
      "binary" -> find_book_number(book: book)
      "number" -> book
    end

  verified =
    if !(found_book > 0 and length(BibleEx.BibleData.last_verse()) >= found_book) do
      false
    else
      verified
    end

  verified
end

@doc ~S"""
Validates a reference consisting of a book and an optional starting chapter.

Checks that:

  * The book exists.
  * The `start_chapter`, if present, is within the book's chapter range.

## Examples

    iex> alias BibleEx.Librarian
    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 1)
    true

    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 999)
    false

"""
def verify_reference(book: book, start_chapter: start_chapter) when not is_nil(book) do
  verified = true

  found_book =
    case BibleEx.typeof(book) do
      "binary" -> find_book_number(book: book)
      "number" -> book
    end

  verified =
    if !(found_book > 0 and length(BibleEx.BibleData.last_verse()) >= found_book) do
      false
    else
      verified
    end

  # if book is not verified at this point, short-circuit
  verified =
    case verified do
      true ->
        if !is_nil(start_chapter) do
          books_last_verse = length(BibleEx.BibleData.last_verse() |> Enum.at(found_book - 1))

          if !(start_chapter > 0 and books_last_verse >= start_chapter) do
            false
          else
            verified
          end
        else
          verified
        end

      false ->
        false
    end

  verified
end

@doc ~S"""
Validates a reference with book, starting chapter, and starting verse.

Checks that:

  * The book exists.
  * The starting chapter exists for that book.
  * The starting verse exists within that chapter.

## Examples

    iex> alias BibleEx.Librarian
    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 1, start_verse: 1)
    true

    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 1, start_verse: 999)
    false

"""
def verify_reference(book: book, start_chapter: start_chapter, start_verse: start_verse)
    when not is_nil(book) do
  verified = true

  found_book =
    case BibleEx.typeof(book) do
      "binary" -> find_book_number(book: book)
      "number" -> book
    end

  verified =
    if !(found_book > 0 and length(BibleEx.BibleData.last_verse()) >= found_book) do
      false
    else
      verified
    end

  # if book is not verified at this point, short-circuit
  verified =
    case verified do
      true ->
        if !is_nil(start_chapter) do
          books_last_verse = length(BibleEx.BibleData.last_verse() |> Enum.at(found_book - 1))

          if !(start_chapter > 0 and books_last_verse >= start_chapter) do
            false
          else
            verified
          end
        else
          verified
        end

      false ->
        false
    end

  # check verse range within the chapter
  verified =
    if is_nil(found_book) do
      false
    else
      case !is_nil(start_verse) do
        true ->
          if !is_nil(start_chapter) do
            books_last_verse_books_start_chapter =
              BibleEx.BibleData.last_verse()
              |> Enum.at(found_book - 1)
              |> Enum.at(start_chapter - 1)

            if !(start_verse > 0 and
                   books_last_verse_books_start_chapter >= start_verse) do
              false
            else
              verified
            end
          else
            false
          end

        false ->
          verified
      end
    end

  verified
end

@doc ~S"""
Validates a reference with book, starting chapter, and ending verse.

This form is used when a verse range is specified in a single chapter
but only the `end_verse` is provided alongside the chapter.

Checks that:

  * The book exists.
  * The chapter exists.
  * The ending verse is within the chapter's verse range.

## Examples

    iex> alias BibleEx.Librarian
    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 1, end_verse: 3)
    true

    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 1, end_verse: 999)
    false

"""
def verify_reference(book: book, start_chapter: start_chapter, end_verse: end_verse)
    when not is_nil(book) do
  verified = true

  found_book =
    case BibleEx.typeof(book) do
      "binary" -> find_book_number(book: book)
      "number" -> book
    end

  verified =
    if !(found_book > 0 and length(BibleEx.BibleData.last_verse()) >= found_book) do
      false
    else
      verified
    end

  # if book is not verified at this point, short-circuit
  verified =
    case verified do
      true ->
        if !is_nil(start_chapter) do
          books_last_verse = length(BibleEx.BibleData.last_verse() |> Enum.at(found_book - 1))

          if !(start_chapter > 0 and books_last_verse >= start_chapter) do
            false
          else
            verified
          end
        else
          verified
        end

      false ->
        false
    end

  verified =
    case !is_nil(end_verse) do
      true ->
        very_last_verse =
          BibleEx.BibleData.last_verse()
          |> Enum.at(found_book - 1)
          |> Enum.at(start_chapter - 1)

        if end_verse <= 0 or very_last_verse < end_verse do
          false
        else
          verified
        end

      false ->
        verified
    end

  verified
end

@doc ~S"""
Validates a fully-specified reference, including optional start and end chapters and verses.

This is the most general form and checks that:

  * The book exists.
  * The start and end chapters are within range and ordered correctly.
  * The start and end verses, if present, are within their chapter ranges and
    ordered correctly relative to each other and the chapter range.

It returns `true` for structurally valid references and `false` otherwise;
it does not check theological or translation-specific constraints.

## Examples

    iex> alias BibleEx.Librarian
    iex> Librarian.verify_reference(book: "John", start_chapter: 3, start_verse: 16, end_chapter: 4, end_verse: 3)
    true

    iex> Librarian.verify_reference(book: "Genesis", start_chapter: 50, start_verse: 1, end_chapter: 49, end_verse: 10)
    false

"""
def verify_reference(
      book: book,
      start_chapter: start_chapter,
      start_verse: start_verse,
      end_chapter: end_chapter,
      end_verse: end_verse
    )
    when not is_nil(book) do
  verified = true

  found_book =
    case BibleEx.typeof(book) do
      "binary" -> find_book_number(book: book)
      "number" -> book
    end

  verified =
    if !(found_book < 1 or (found_book > 0 and length(BibleEx.BibleData.last_verse()) >= found_book)) do
      false
    else
      verified
    end

  verified =
    if is_nil(found_book) do
      false
    else
      if !is_nil(start_chapter) do
        books_last_verse = length(BibleEx.BibleData.last_verse() |> Enum.at(found_book - 1))

        if !(start_chapter > 0 and books_last_verse >= start_chapter) do
          false
        else
          verified
        end

        if !is_nil(end_chapter) and start_chapter > end_chapter do
          false
        else
          verified
        end
      else
        if !is_nil(end_chapter) or !is_nil(end_verse) do
          false
        else
          verified
        end
      end

      verified =
        case !is_nil(start_verse) do
          true ->
            books_last_verse_books_start_chapter =
              BibleEx.BibleData.last_verse()
              |> Enum.at(found_book - 1)
              |> Enum.at(start_chapter - 1)

            if !(start_verse > 0 and books_last_verse_books_start_chapter >= start_verse) do
              false
            else
              verified
            end

            if !is_nil(end_verse) and !is_nil(start_verse) do
              if start_verse > end_verse do
                false
              else
                verified
              end
            else
              verified
            end

          false ->
            verified
        end

      verified =
        case !is_nil(end_chapter) do
          true ->
            books_last_verse = length(BibleEx.BibleData.last_verse() |> Enum.at(found_book - 1))

            if !(books_last_verse >= end_chapter) do
              false
            else
              verified
            end

          false ->
            verified
        end

      verified =
        case !is_nil(end_verse) do
          true ->
            if is_nil(end_chapter) do
              false
            else
              very_last_verse =
                BibleEx.BibleData.last_verse()
                |> Enum.at(found_book - 1)
                |> Enum.at(end_chapter - 1)

              if end_verse > 0 and very_last_verse >= end_verse do
                verified
              else
                if end_verse < start_verse do
                  false
                else
                  verified
                end
              end
            end

          false ->
            verified
        end

      verified
    end

  verified
end



  @doc ~S"""
  Renders a human-readable reference string from its components.

  This family of functions builds strings such as:

    * `"Genesis 1"`
    * `"Genesis 1:1"`
    * `"John 3:16 - 4:3"`
    * `"Genesis 1-2"`

  Depending on which arguments are provided, the output adjusts to match
  a conventional Bible reference format. If the inputs are not sufficient
  to build a reference (for example, missing book and chapter), `nil` is returned.

  ## Examples

      iex> alias BibleEx.Librarian
      iex> Librarian.create_reference_string(book: "Genesis", start_chapter: 1)
      "Genesis 1"

      iex> Librarian.create_reference_string(book: "Genesis", start_chapter: 1, start_verse: 1)
      "Genesis 1:1"

      iex> Librarian.create_reference_string(book: "John", start_chapter: 3, start_verse: 16, end_chapter: 4, end_verse: 3)
      "John 3:16 - 4:3"

  """
  def create_reference_string(book: _book) do
    nil
  end

  def create_reference_string(book: book, start_chapter: start_chapter) do
    reference = ""

    reference =
      case !is_nil(book) and !is_nil(start_chapter) do
        true  -> reference <> book <> " #{start_chapter}"
        false -> reference
      end

    if(reference == "", do: nil, else: reference)
  end

  def create_reference_string(book: book, start_chapter: start_chapter, start_verse: start_verse) do
    reference = ""

    reference =
      case !is_nil(book) and !is_nil(start_chapter) do
        true ->
          reference = reference <> book <> " #{start_chapter}"

          case !is_nil(start_verse) do
            true  -> reference <> ":#{start_verse}"
            false -> reference
          end

        false ->
          reference
      end

    if(reference == "", do: nil, else: reference)
  end

  def create_reference_string(
        book: book,
        start_chapter: start_chapter,
        start_verse: start_verse,
        end_chapter: end_chapter
      ) do
    reference = ""

    reference =
      case !is_nil(book) and !is_nil(start_chapter) do
        true ->
          reference = reference <> book <> " #{start_chapter}"

          if !is_nil(start_verse) and !is_nil(end_chapter) and end_chapter != start_chapter do
            reference = reference <> ":#{start_verse}"
            reference = reference <> " - #{end_chapter}"

            end_chapter_last_verse = get_last_verse_number(book: book, chapter: end_chapter)

            if !is_nil(end_chapter_last_verse) do
              reference = reference <> ":#{end_chapter_last_verse}"
              reference
            else
              reference
            end
          else
            reference =
              if !is_nil(end_chapter) and end_chapter != start_chapter do
                reference = reference <> "-#{end_chapter}"
                reference
              else
                reference
              end

            reference
          end

        false ->
          reference
      end

    if(reference == "", do: nil, else: reference)
  end

  def create_reference_string(
        book: book,
        start_chapter: start_chapter,
        start_verse: start_verse,
        end_chapter: end_chapter,
        end_verse: end_verse
      ) do
    reference = ""

    reference =
      case !is_nil(book) and !is_nil(start_chapter) do
        true ->
          reference = reference <> book <> " #{start_chapter}"

          if !is_nil(start_verse) and !is_nil(end_chapter) and end_chapter != start_chapter do
            reference = reference <> ":#{start_verse}"
            reference = reference <> " - #{end_chapter}"

            if !is_nil(end_verse) do
              reference = reference <> ":#{end_verse}"
              reference
            else
              end_chapter_last_verse = get_last_verse_number(book: book, chapter: end_chapter)

              if !is_nil(end_chapter_last_verse) do
                reference = reference <> ":#{end_chapter_last_verse}"
                reference
              else
                reference
              end
            end
          else
            reference =
              if !is_nil(end_verse) and is_nil(start_verse) do
                reference = reference <> ":1"
                reference
              else
                if !is_nil(start_verse) do
                  reference = reference <> ":#{start_verse}"
                  reference
                else
                  reference
                end
              end

            reference =
              if !is_nil(end_chapter) do
                reference =
                  if is_nil(start_verse) and is_nil(end_verse) do
                    reference =
                      if start_chapter != end_chapter do
                        reference <> "-#{end_chapter}"
                      else
                        reference
                      end

                    reference
                  else
                    if start_chapter != end_chapter do
                      reference <> " - #{end_chapter}"
                    else
                      reference
                    end

                    reference
                  end

                reference
              else
                reference
              end

            reference =
              if !is_nil(end_verse) do
                reference =
                  if !is_nil(start_verse) do
                    reference <> "-#{end_verse}"
                  else
                    if !is_nil(end_chapter) do
                      reference <> " - #{end_chapter}:#{end_verse}"
                    else
                      reference <> ":#{end_verse}"
                    end
                  end

                reference
              else
                reference
              end

            reference
          end

        false ->
          reference =
            if is_nil(start_chapter) and is_nil(start_verse) do
              book
            else
              reference
            end

          reference
      end

    if(reference == "", do: nil, else: reference)
  end
end