require 'pg'

module Shiba
  class Connection
    class Postgres
      def initialize(h)
        @connection = PG.connect( dbname: h['database'], host: h['host'], user: h['username'], password: h['password'], port: h['port'] )
        @connection.type_map_for_results = PG::BasicTypeMapForResults.new(@connection)
        query("SET enable_seqscan = OFF")
        query("SET random_page_cost = 0.01")
      end

      def query(sql)
        @connection.query(sql)
      end

      def fetch_indexes
        result = query(<<-EOL
          select
              t.relname as table_name,
              i.relname as index_name,
              a.attname as column_name,
              i.reltuples as numrows,
              ix.indisunique as is_unique,
              ix.indisprimary as is_primary,
              s.n_distinct as numdistinct
          from pg_namespace p
          join pg_class t on t.relnamespace = p.oid
          join pg_index ix on ix.indrelid = t.oid
          join pg_class i on i.oid = ix.indexrelid
          join pg_attribute a on a.attrelid = t.oid
          left join pg_stats s on s.tablename = t.relname
              AND s.attname = a.attname
          where
              p.nspname = 'public'
              and a.attnum = ANY(ix.indkey)
              and t.relkind = 'r'
          order by
              t.relname,
              ix.indisprimary desc,
              i.relname,
              array_position(ix.indkey, a.attnum)
          EOL
        )
        rows = result.to_a.map do |row|
          # TBD: do better than this, have them return something objecty
          if row['is_primary'] == "t"
            row['index_name'] = "PRIMARY"
            row['non_unique'] = 0
          elsif row['is_unique']
            row['non_unique'] = 0
          end

          if row['numdistinct'].nil?
            # meaning the table's empty.
            row['cardinality'] = 0
          elsif row['numdistinct'] == 0
            # numdistinct is 0 if there's rows in the table but all values are null
            row['cardinality'] = 1
          elsif row['numdistinct'] < 0
            # postgres talks about either cardinality or selectivity (depending.  what's their heuristic?)
            # in the same way we do in the yaml file!
            # if less than zero, it's negative selectivity.
            row['cardinality'] = -(row['numrows'] * row['numdistinct'])
          else
            row['cardinality'] = row['numdistinct']
          end
          row
        end

        #TODO: estimate multi-index column cardinality
        rows
      end

      def count_indexes_by_table
        sql = <<-EOL
          select tablename as table_name, count(*) as index_count from pg_indexes where schemaname='public' group by 1 order by 2
        EOL
        @connection.query(sql).to_a
      end

      def explain(sql)
        rows = query("EXPLAIN (FORMAT JSON) #{sql}").to_a
        [rows.first["QUERY PLAN"], {}]
      end

      def each_column_size
      end

      def mysql?
        false
      end
    end
  end
end
