# Allow usage of Common Table Expressions (CTE) in the query building
module Clear::SQL::Query::CTE
  # :nodoc:
  alias CTEAuthorized = Clear::SQL::SelectBuilder | String

  record Record, query : CTEAuthorized, recursive : Bool

  # List the current CTE of the query. The key is the name of the CTE,
  # while the value is the fragment (string or Sub-select)
  getter cte : Hash(String, Clear::SQL::Query::CTE::Record) = {} of String => Clear::SQL::Query::CTE::Record

  # Add a CTE to the query.
  #
  # ```crystal
  # Clear::SQL.select.with_cte("full_year",
  #   "SELECT DATE(date)"
  #   "FROM generate_series(NOW() - INTERVAL '1 year', NOW(), '1 day'::interval) date")
  #   .select("*").from("full_year")
  # # WITH full_year AS ( SELECT DATE(date) ... ) SELECT * FROM full_year;
  # ```
  def with_cte(name, request : CTEAuthorized, recursive = false)
    cte[name] = Record.new(request, recursive)
    change!
  end

  # Add a CTE to the query. Use NamedTuple convention:
  #
  # ```crystal
  # Clear::SQL.select.with_cte(cte: "xxx")
  # # WITH cte AS xxx SELECT...
  # ```
  def with_cte(tuple : NamedTuple)
    tuple.each do |k, v|
      cte[k.to_s] = Record.new(v, false)
    end
    change!
  end

  # Add a CTE to the query. Use NamedTuple convention:
  #
  # ```crystal
  # Clear::SQL.select.with_recursive_cte(cte: "xxx")
  # # WITH RECURSIVE cte AS xxx SELECT...
  # ```
  def with_recursive_cte(tuple : NamedTuple)
    tuple.each do |k, v|
      cte[k.to_s] = Record.new(v, true)
    end
    change!
  end

  # :nodoc:
  protected def print_ctes
    return unless cte.any?

    {"WITH ",
     cte.map do |name, v|
       query = v.query
       value = query.responds_to?(:to_sql) ? query.to_sql : query.to_s
       {v.recursive ? "RECURSIVE" : "",
        name, " AS (", value, ")"}.join
     end.join(", "),
    }.join
  end
end
