# frozen_string_literal: true

module ActiveRecord
  module ConnectionAdapters
    module OracleEnhanced
      module Quoting
        # QUOTING ==================================================
        #
        # see: abstract/quoting.rb

        def quote_column_name(name) #:nodoc:
          name = name.to_s
          @quoted_column_names[name] ||= begin
            # if only valid lowercase column characters in name
            if name =~ /\A[a-z][a-z_0-9\$#]*\Z/
              "\"#{name.upcase}\""
            else
              # remove double quotes which cannot be used inside quoted identifier
              "\"#{name.gsub('"', '')}\""
            end
          end
        end

        # This method is used in add_index to identify either column name (which is quoted)
        # or function based index (in which case function expression is not quoted)
        def quote_column_name_or_expression(name) #:nodoc:
          name = name.to_s
          case name
          # if only valid lowercase column characters in name
          when /^[a-z][a-z_0-9\$#]*$/
            "\"#{name.upcase}\""
          when /^[a-z][a-z_0-9\$#\-]*$/i
            "\"#{name}\""
          # if other characters present then assume that it is expression
          # which should not be quoted
          else
            name
          end
        end

        # Used only for quoting database links as the naming rules for links
        # differ from the rules for column names. Specifically, link names may
        # include periods.
        def quote_database_link(name)
          case name
          when NONQUOTED_DATABASE_LINK
            %Q("#{name.upcase}")
          else
            name
          end
        end

        # Names must be from 1 to 30 bytes long with these exceptions:
        # * Names of databases are limited to 8 bytes.
        # * Names of database links can be as long as 128 bytes.
        #
        # Nonquoted identifiers cannot be Oracle Database reserved words
        #
        # Nonquoted identifiers must begin with an alphabetic character from
        # your database character set
        #
        # Nonquoted identifiers can contain only alphanumeric characters from
        # your database character set and the underscore (_), dollar sign ($),
        # and pound sign (#). Database links can also contain periods (.) and
        # "at" signs (@). Oracle strongly discourages you from using $ and # in
        # nonquoted identifiers.
        NONQUOTED_OBJECT_NAME   = /[[:alpha:]][\w$#]{0,29}/
        NONQUOTED_DATABASE_LINK = /[[:alpha:]][\w$#\.@]{0,127}/
        VALID_TABLE_NAME = /\A(?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}(?:@#{NONQUOTED_DATABASE_LINK})?\Z/

        # unescaped table name should start with letter and
        # contain letters, digits, _, $ or #
        # can be prefixed with schema name
        # CamelCase table names should be quoted
        def self.valid_table_name?(name) #:nodoc:
          object_name = name.to_s
          !!(object_name =~ VALID_TABLE_NAME && !mixed_case?(object_name))
        end

        def self.mixed_case?(name)
          object_name = name.include?(".") ? name.split(".").second : name
          !!(object_name =~ /[A-Z]/ && object_name =~ /[a-z]/)
        end

        def quote_table_name(name) #:nodoc:
          name, link = name.to_s.split("@")
          @quoted_table_names[name] ||= [name.split(".").map { |n| quote_column_name(n) }.join("."), quote_database_link(link)].compact.join("@")
        end

        def quote_string(s) #:nodoc:
          s.gsub(/'/, "''")
        end

        def _quote(value) #:nodoc:
          case value
          when Type::OracleEnhanced::NationalCharacterString::Data then
            "N".dup << "'#{quote_string(value.to_s)}'"
          when ActiveModel::Type::Binary::Data then
            "empty_blob()"
          when Type::OracleEnhanced::Text::Data then
            "empty_clob()"
          when Type::OracleEnhanced::NationalCharacterText::Data then
            "empty_nclob()"
          else
            super
          end
        end

        def quoted_true #:nodoc:
          return "'Y'" if emulate_booleans_from_strings
          "1".freeze
        end

        def unquoted_true #:nodoc:
          return "Y" if emulate_booleans_from_strings
          "1".freeze
        end

        def quoted_false #:nodoc:
          return "'N'" if emulate_booleans_from_strings
          "0".freeze
        end

        def unquoted_false #:nodoc:
          return "N" if emulate_booleans_from_strings
          "0".freeze
        end

        def _type_cast(value)
          case value
          when Type::OracleEnhanced::TimestampTz::Data, Type::OracleEnhanced::TimestampLtz::Data
            if value.acts_like?(:time)
              zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
              value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
            else
              value
            end
          when Type::OracleEnhanced::NationalCharacterString::Data
            value.to_s
          else
            super
          end
        end

        private

          def oracle_downcase(column_name)
            return nil if column_name.nil?
            column_name =~ /[a-z]/ ? column_name : column_name.downcase
          end
      end
    end
  end
end

# if MRI or YARV
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
  require "active_record/connection_adapters/oracle_enhanced/oci_quoting"
# if JRuby
elsif RUBY_ENGINE == "jruby"
  require "active_record/connection_adapters/oracle_enhanced/jdbc_quoting"
else
  raise "Unsupported Ruby engine #{RUBY_ENGINE}"
end