# internal shortcut alias
doc() {
  rlx.commands.doc "$@";
}

# main
rlx.commands.doc() {
  if [ $# -eq 0 ]; then
    if [ -z "${info[id]:-}" ]; then
      console error -- "no document selected";
    else
      console info -- "using document %s" "${info[id]}";
    fi
  else
    local doc_namespace="doc.commands";
    local cmd="${1:-}"; shift;
    if ! method.exists? "${doc_namespace}.${cmd}"; then
      doc.commands.use "${cmd}" "$@";
    else
      delegate "${doc_namespace}" "${cmd}" "$@";
    fi
  fi
}

# use a document
doc.commands.use() {
  local id="${1:-}";
  local db="${2:-${info[database]:-}}";
  if [ -z "${db:-}" ]; then
    console error -- "no database selected";
  else
    info[id]="${id}";
    doc etag "${id}" "${db}" false >/dev/null 2>&1;
    if ! ${flags[success]}; then
      info[revision]="";
      if [ "${http[status]:-}" == 404 ]; then
        console warn -- "document %s does not exist" "${id}";
      fi
    fi
    rlx.commands.pwd;
  fi
}

# edit a document
doc.commands.edit() {
  local id="${1:-${info[id]:-}}";
  local rev="${2:-}";
  local db="${3:-${info[database]:-}}";
  if [ -z "${db:-}" ]; then
    console error -- "no database selected";
  elif [ -z "${id}" ]; then
    console error -- "no document identifier specified";
  else
    #if [ -z "${rev}" ]; then
      #echo "fetch revision for edit...";
      #return 1;
    #fi
    db context "${db}" doc get "${id}" "${rev:-}" false;
    if ${flags[success]}; then
      rlx.interpreter;
      if rlx.json.enabled "${rlx_settings[lang]:-}" \
        && [ -x "${interpreter}" ]; then
        "$interpreter" < "${files[response]}" >| "${files[input]}";
      else
        cp -f "${files[response]}" "${files[input]}" \
          || {
            console error -- "could not copy response document" \
            && return 0;
          }
      fi
      doc add "${id}" "${db}" "${files[input]}";
    fi
  fi
}

# create a new document
doc.commands.add() {
  local id="${1:-${info[id]:-}}";
  local db="${2:-${info[database]:-}}";
  local file="${3:-}";
  local template="${rlx_settings[tpldoc]:-doc}";
  if [ -z "${db}" ]; then
    console error -- "no database selected";
  elif [ -z "${id}" ]; then
    console error -- "no document identifier specified";
  else
    if [ -z "${file}" ]; then
      tpl clean;
      tpl parse "${template}" false id=${id};
      file="${files[input]}";
    fi
    rlx.file.cleanup "${files[document]}";
    saved() {
      rlx.run couchdb.doc.save \
        "${info[server]}" "${db}" \
        "${files[document]}" "${id}";
      if ${flags[success]}; then
        rlx.json.print;
        doc.commands.etag >/dev/null 2>&1;
      fi
    }
    rlx.edit "${file}";
  fi
}

# get a document
doc.commands.get() {
  local id="${1:-${info[id]:-}}"; shift;
  local rev="${1:-}"; shift;
  local print="${print:-true}";
  if [ "${rev}" == true ] || [ "${rev}" == false ]; then
    print="${rev}";
    rev="";
  else
    if [ "${1:-}" == true ] || [ "${3:-}" == false ]; then
      print="${1:-}"; shift;
    fi
  fi
  #echo "doc get $*"
  local params="${@:3}";
  local db="${info[database]:-}";
  if [ -z "${db:-}" ]; then
    console error -- "no database selected";
  else
    if [ -z "${id}" ]; then
      console error -- "no id specified";
    else
      rlx.run couchdb.doc.get \
        "${info[server]}" "${db}" "${id}" \
        "--rev=${rev}" ${params};
      if ${flags[success]} && $print; then
        rlx.json.print;
      fi
    fi
  fi
}

# get a document with revision information
doc.commands.revsinfo() {
  doc get "${1:-}" "${2:-}" true "--revs_info=true";
}

# get a document with conflicts
doc.commands.conflicts() {
  doc get "${1:-}" "${2:-}" true "--conflicts=true" \
    "--deleted_conflicts=true";
}

# get a document with revisions
doc.commands.revs() {
  doc get "${1:-}" "${2:-}" true "--revs=true";
}

# get a document with sequence information
doc.commands.seq() {
  doc get "${1:-}" "${2:-}" true "--local_seq=true";
}

# get open revisions
doc.commands.openrevs() {
  local revisions=( ${@:2} );
  local revs="all";
  if [ ${#revisions[@]} -gt 0 ]; then
    tpl clean;
    tpl array "revs" ${@:2};
    tpl compact open-revs false;
    revs=$( cat "${files[input]}" );
  fi
  doc get "${1:-}" "" true "--open_revs=${revs}";
}

# copy a document
doc.commands.copy() {
  local id="${1:-}";
  local target="${2:-}";
  local rev="${3:-}";
  if [ -z "${info[database]:-}" ]; then
    console error -- "no database selected";
  elif [ -z "${id}" ]; then
    console error -- "no source document identifier specified";
  elif [ -z "${target}" ]; then
    console error -- "no target document identifier specified";
  else
    # check for the existence of the target document
    if [ -z "${rev}" ]; then
      doc etag "${target}" "${info[database]}" false 2>/dev/null;
      if ${flags[success]} && [ -n "${info[revision]:-}" ]; then
        rev="${info[revision]:-}";
        # TODO: prompt to overwrite existing document here!!!
      elif [ "${http[status]}" != 404 ]; then
        console error -- "unknown error fetching target revision";
      fi
    fi
    #echo "copy with revision... $rev"
    rlx.run couchdb.doc.copy \
      "${info[server]}" "${info[database]}" \
      "${id}" "${target}" "${rev}";
    if ${flags[success]}; then
      console info -- "copied %s to %s" "${id}" "${target}";
    fi
  fi
}

# print the document revision
doc.commands.etag() {
  local id="${1:-${info[id]:-}}";
  local db="${2:-${info[database]:-}}";
  local print="${3:-true}";
  if [ -z "${id}" ]; then
    console error -- "no document identifier available";
  else
    doc head "${id}" "${db}" true;
    if ${flags[success]}; then
      if $print && [ -n "${info[revision]:-}" ]; then
        printf "${info[revision]:-}\n";
      fi
    else
      console error -- "could not retrieve document revision";
    fi
  fi
}

# document head
doc.commands.head() {
  local id="${1:-${info[id]:-}}";
  local db="${2:-${info[database]:-}}";
  local etag=${3:-false};
  local print="${print:-true}";
  if [ -z "${db:-}" ]; then
    console error -- "no database selected";
  else
    if [ -z "${id}" ]; then
      console error -- "no id specified";
    else
      rlx.run couchdb.doc.head \
        "${info[server]}" "${db}" "${id}";
      if ${flags[success]}; then
        if $etag; then
          # NOTE: use an array to strip leading whitespace
          local rev="$(etag.parse)";
          info[revision]="${rev}";
        else
          if [ "${rlx_settings[resheaders]:-}" == false ] && $print; then
            rlx.resheaders;
          fi
        fi
      fi
    fi
  fi
}

# list documents via all docs
doc.commands.ls() {
  local db="${1:-${info[database]:-}}";
  if [ -z "${db:-}" ]; then
    console error -- "no database selected";
  else
    local querystring;
    design.alldocs.querystring;
    rlx.run couchdb.db.alldocs \
      "${info[server]}" "${db}" "${querystring}";
    if ${flags[success]}; then
      local subcommand="ls";
      if [ -n "${include_docs:-}" ]; then
        subcommand="list";
      fi
      rlx.json.paginate "${files[response]:-}" "doc $subcommand $*";
    fi
  fi
}

# list documents and include docs
doc.commands.list() {
  local include_docs=true;
  doc ls "$@";
}

# delegate design document commands
doc.commands.design() {
  local namespace="doc.design.commands";
  local cmd="${1:-}"; shift;
  if ! method.exists? "${namespace}.${cmd}"; then
    console error -- "unknown doc design command %s" "${cmd}";
  else
    delegate "${namespace}" "${cmd}" "$@";
  fi
}

# list design documents
doc.design.commands.ls() {
  local db="${1:-${info[database]}}";
  if [ -z "${db}" ]; then
    console error -- "no database specified";
  else
    local template="query/design";
    local startkey="_design/";
    local endkey="_design0";
    design.query.parse false false;
    local querystring;
    design.querystring "${files[input]}";
    rlx.run couchdb.db.alldocs \
      "${info[server]}" "${db}" "${querystring}";
    if ${flags[success]}; then
      #rlx.json.print;
      local subcommand="ls";
      if [ -n "${include_docs:-}" ]; then
        subcommand="list";
      fi
      rlx.json.paginate "${files[response]:-}" \
        "doc design $subcommand $*";
    fi
  fi
}

# list design documents and include docs
doc.design.commands.list() {
  local include_docs=true;
  doc design ls "$@";
}

# delete a document
doc.commands.rm() {
  local id="${1:-${info[id]:-}}";
  local db="${2:-${info[database]:-}}";
  local force="${force:-false}";
  if [ -z "${db:-}" ]; then
    console error -- "no database specified";
  else
    if [ -z "${id}" ]; then
      console error -- "no id specified";
    else
      local server="${info[server]}";
      accepted() {
        doc etag "${id}" "${db}" false;
        if ${flags[success]}; then
          local rev="${info[revision]:-}";
          rlx.run couchdb.doc.rm \
            "${server}" "${db}" "${id}" "${rev}";
          if ${flags[success]}; then
            console info -- "document %s deleted" "${id}";
          fi
        #else
          #console error -- "could not retrieve document revision";
        fi
        rlx.prompt;
      }
      if ! $force; then
        console prompt --program \
          "delete document %s? (y/n)" "${id}";
        prompt confirm \
          --accepted=accepted \
          --rejected=rlx.rejected \
          --id=delete;
      else
        accepted;
      fi
    fi
  fi
}

etag.parse() {
  local rev=( ${http_res_headers[ETag]:-} );
  rev="${rev[0]:-}";
  rlx.unquote "${rev}" "rev";
  printf "${rev}";
}
