local cjson_safe    = require "cjson.safe"


local kong          = kong
local encode_base64 = ngx.encode_base64
local decode_base64 = ngx.decode_base64
local fmt           = string.format
local unpack        = unpack


local Tags = {}


local sql_templates = {
  page_first = [[
  SELECT entity_id, entity_name, tag, ordinality
    FROM tags,
    UNNEST(tags) WITH ORDINALITY as t_tag (tag, ordinality)
    ORDER BY entity_id
    LIMIT %s;]],
  page_next  = [[
  SELECT entity_id, entity_name, tag, ordinality
    FROM tags,
    UNNEST(tags) WITH ORDINALITY as t_tag (tag, ordinality)
    WHERE entity_id > %s OR (entity_id = %s AND ordinality > %s)
    ORDER BY entity_id
    LIMIT %s;]],
  page_for_tag_first = [[
  SELECT entity_id, entity_name
    FROM tags
    WHERE %s = ANY(tags)
    ORDER BY entity_id
    LIMIT %s;]],
  page_for_tag_next  = [[
  SELECT entity_id, entity_name
    FROM tags
    WHERE entity_id > %s AND %s = ANY(tags)
    ORDER BY entity_id
    LIMIT %s;]],
}


local function page(self, size, token, options, tag)
  if not size then
    size = self.connector:get_page_size(options)
  end

  local limit = size + 1

  local sql
  local args

  local tag_literal
  if tag then
    tag_literal = self:escape_literal(tag)
  end

  if token then
    local token_decoded = decode_base64(token)
    if not token_decoded then
      return nil, self.errors:invalid_offset(token, "bad base64 encoding")
    end

    token_decoded = cjson_safe.decode(token_decoded)
    if not token_decoded then
      return nil, self.errors:invalid_offset(token, "bad json encoding")
    end

    local entity_id_delimeter = self:escape_literal(token_decoded[1])

    if tag then
      sql = sql_templates.page_for_tag_next
      args = {
                entity_id_delimeter,
                tag_literal, limit
              }
    else
      sql = sql_templates.page_next
      local ordinality_delimeter = self:escape_literal(token_decoded[2])
      args = {
                entity_id_delimeter, entity_id_delimeter,
                ordinality_delimeter, limit
              }
    end
  else
    if tag then
      sql = sql_templates.page_for_tag_first
      args = { tag_literal, limit  }
    else
      sql = sql_templates.page_first
      args = { limit }
    end
  end

  sql = fmt(sql, unpack(args))

  local res, err = self.connector:query(sql, "read")

  if not res then
    return nil, self.errors:database_error(err)
  end

  local rows = kong.table.new(size, 0)

  local last_ordinality

  for i = 1, limit do
    local row = res[i]
    if not row then
      break
    end

    if i == limit then
      row = res[size]
      local offset = {
        row.entity_id,
        last_ordinality
      }

      offset = cjson_safe.encode(offset)
      offset = encode_base64(offset, true)

      return rows, nil, offset
    end

    last_ordinality = row.ordinality
    row.ordinality = nil

    if tag then
      row.tag = tag
    end
    rows[i] = self.expand(row)
  end

  return rows

end


function Tags:page_by_tag(tag, size, token, options)
  return page(self, size, token, options, tag)
end


-- Overwrite the page function for /tags
function Tags:page(size, token, options)
  return page(self, size, token, options)
end


return Tags
