module Jennifer
  module Adapter
    module Quoting
      ARGUMENT_ESCAPE_STRING  = "%s"
      STRING_QUOTING_PATTERNS = {'\\' => "\\\\", '\'' => "''", '"' => "\\\""}

      # Quotes the column value to help prevent -[SQL injection attacks][https://en.wikipedia.org/wiki/SQL_injection].
      abstract def quote(value : String)

      # Quotes the given identifier according to the language specification to prevent overlappings with predefined
      # keywords.
      abstract def quote_identifier(identifier : String | Symbol)

      # Quotes the given table name according to the language specification.
      #
      # Dot inside of name is allowed to specify schema name.
      abstract def quote_table(table : String)

      def quote_identifiers(identifiers)
        identifiers.map { |id| quote_identifier(id) }
      end

      abstract def quote_json_string(value : String)

      # :ditto:
      def quote(value : Nil)
        "NULL"
      end

      # :ditto:
      def quote(value : Bool)
        value ? "TRUE" : "FALSE"
      end

      # :ditto:
      def quote(value : Int | Float | UInt32)
        value.to_s
      end

      # :ditto:
      def quote(value : Char)
        quote(value.to_s)
      end

      # :ditto:
      def quote(value : Time)
        "'#{value.to_utc.to_s("%F %T")}'"
      end

      # :ditto:
      def quote(value : Time::Span)
        # NOTE: isn't user by pg driver ATM
        "'#{value}'"
      end

      # :ditto:
      def quote(value : Slice(UInt8))
        "x'#{value.hexstring}'"
      end

      # :ditto:
      def quote(value : JSON::Any)
        "'" + ::Jennifer::Adapter::JSONEncoder.encode(value, self) + "'"
      end

      # :ditto:
      def quote(value : JSON::PullParser)
        "'" + ::Jennifer::Adapter::JSONEncoder.encode(JSON::Any.from_json(value.read_raw), self) + "'"
      end

      # :ditto:
      def quote(value)
        raise ArgumentError.new("Value #{value} can't be quoted")
      end

      # Quotes strings of JSON column value to help prevent -[SQL injection attacks][https://en.wikipedia.org/wiki/SQL_injection].
      def quote_json_string(value : String)
        value.gsub('\'', "''")
      end

      def filter_out(arg)
        escape_string
      end

      def escape_string
        ARGUMENT_ESCAPE_STRING
      end

      def escape_string(size : Int32)
        case size
        when 1
          ARGUMENT_ESCAPE_STRING
        when 2
          "#{ARGUMENT_ESCAPE_STRING}, #{ARGUMENT_ESCAPE_STRING}"
        when 3
          "#{ARGUMENT_ESCAPE_STRING}, #{ARGUMENT_ESCAPE_STRING}, #{ARGUMENT_ESCAPE_STRING}"
        else
          size.times.join(", ") { ARGUMENT_ESCAPE_STRING }
        end
      end

      def filter_out(arg : Array, single : Bool = true)
        single ? escape_string : arg.join(", ") { |a| filter_out(a) }
      end

      def filter_out(arg : QueryBuilder::SQLNode)
        arg.as_sql(self)
      end
    end
  end
end
