module DuckDB::Converter

def _decimal_to_unscaled(value, scale)

def _decimal_to_unscaled(value, scale)
  (value * (10**scale)).to_i
end

def _decimal_width(value)

def _decimal_width(value)
  value.to_s('F').gsub(/[^0-9]/, '').length
end

def _hugeint_lower(value)

def _hugeint_lower(value)
  value & LOWER_HUGEINT_MASK
end

def _hugeint_upper(value)

def _hugeint_upper(value)
  value >> HALF_HUGEINT_BIT
end

def _parse_date(value)

def _parse_date(value)
  case value
  when Date, Time
    value
  else
    Date.parse(value)
  end
rescue StandardError => e
  raise(ArgumentError, "Cannot parse `#{value.inspect}` to Date object. #{e.message}")
end

def _parse_deciaml(value)

def _parse_deciaml(value)
  case value
  when BigDecimal
    value
  else
    begin
      BigDecimal(value.to_s)
    rescue StandardError => e
      raise(ArgumentError, "Cannot parse `#{value.inspect}` to BigDecimal object. #{e.message}")
    end
  end
end

def _parse_time(value)

def _parse_time(value)
  case value
  when Time
    value
  when DateTime
    value.to_time
  else
    Time.parse(value)
  end
rescue StandardError => e
  raise(ArgumentError, "Cannot parse `#{value.inspect}` to Time object. #{e.message}")
end

def _to_date(year, month, day)

def _to_date(year, month, day)
  Date.new(year, month, day)
end

def _to_decimal_from_hugeint(width, scale, upper, lower = nil)

def _to_decimal_from_hugeint(width, scale, upper, lower = nil)
  v = lower.nil? ? upper : _to_hugeint_from_vector(lower, upper)
  _to_decimal_from_value(width, scale, v)
end

def _to_decimal_from_value(_width, scale, value)

def _to_decimal_from_value(_width, scale, value)
  BigDecimal("#{value}e-#{scale}")
end

def _to_hugeint_from_vector(lower, upper)

def _to_hugeint_from_vector(lower, upper)
  (upper << HALF_HUGEINT_BIT) + lower
end

def _to_infinity(value)

def _to_infinity(value)
  if value.positive?
    DuckDB::Infinity::POSITIVE
  else
    DuckDB::Infinity::NEGATIVE
  end
end

def _to_interval_from_vector(months, days, micros)

def _to_interval_from_vector(months, days, micros)
  Interval.new(interval_months: months, interval_days: days, interval_micros: micros)
end

def _to_query_progress(percentage, rows_processed, total_rows_to_process)

def _to_query_progress(percentage, rows_processed, total_rows_to_process)
  DuckDB::QueryProgress.new(percentage, rows_processed, total_rows_to_process).freeze
end

def _to_time(year, month, day, hour, minute, second, microsecond)

rubocop:disable Metrics/ParameterLists
def _to_time(year, month, day, hour, minute, second, microsecond)
  Time.public_send(
    default_timezone_utc? ? :utc : :local,
    year, month, day, hour, minute, second, microsecond
  )
end

def _to_time_from_duckdb_time(hour, minute, second, microsecond)

def _to_time_from_duckdb_time(hour, minute, second, microsecond)
  return Time.utc(1970, 1, 1, hour, minute, second, microsecond) if default_timezone_utc?
  Time.parse(
    format(
      '%<hour>02d:%<minute>02d:%<second>02d.%<microsecond>06d',
      hour: hour,
      minute: minute,
      second: second,
      microsecond: microsecond
    )
  )
end

def _to_time_from_duckdb_time_ns(nanos)

def _to_time_from_duckdb_time_ns(nanos)
  hour = nanos / 3_600_000_000_000
  nanos %= 3_600_000_000_000
  min = nanos / 60_000_000_000
  nanos %= 60_000_000_000
  sec = nanos / 1_000_000_000
  microsecond = (nanos % 1_000_000_000) / 1_000
  _to_time_from_duckdb_time(hour, min, sec, microsecond)
end

def _to_time_from_duckdb_time_tz(hour, min, sec, micro, timezone)

def _to_time_from_duckdb_time_tz(hour, min, sec, micro, timezone)
  tz_offset = format_timezone_offset(timezone)
  time_str = format('%<hour>02d:%<min>02d:%<sec>02d.%<micro>06d%<tz>s',
                    hour: hour, min: min, sec: sec, micro: micro, tz: tz_offset)
  Time.parse(time_str)
end

def _to_time_from_duckdb_timestamp_ms(time)

def _to_time_from_duckdb_timestamp_ms(time)
  _to_time_from_duckdb_timestamp_s(time / 1000).then do |tm|
    _to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1000 * 1000)
  end
end

def _to_time_from_duckdb_timestamp_ns(time)

def _to_time_from_duckdb_timestamp_ns(time)
  _to_time_from_duckdb_timestamp_s(time / 1_000_000_000).then do |tm|
    _to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1_000_000_000 / 1000)
  end
end

def _to_time_from_duckdb_timestamp_s(time)

def _to_time_from_duckdb_timestamp_s(time)
  if default_timezone_utc?
    EPOCH_UTC + time
  else
    EPOCH + time
  end
end

def _to_time_from_duckdb_timestamp_tz(bits)

def _to_time_from_duckdb_timestamp_tz(bits)
  micro = bits % 1_000_000
  time = EPOCH_UTC + (bits / 1_000_000)
  timestamp_str = format_timestamp_with_micro(time, micro)
  Time.parse(timestamp_str)
end

def decimal_to_hugeint(value)

def decimal_to_hugeint(value)
  integer_value = (value * (10**value.scale)).to_i
  integer_to_hugeint(integer_value)
rescue FloatDomainError => e
  raise(ArgumentError, "The argument `#{value.inspect}` must be converted to Integer. #{e.message}")
end

def default_timezone_utc?

def default_timezone_utc?
  defined?(DuckDB.default_timezone) && DuckDB.default_timezone == :utc
end

def format_timestamp_with_micro(time, micro)

def format_timestamp_with_micro(time, micro)
  format('%<year>04d-%<mon>02d-%<day>02d %<hour>02d:%<min>02d:%<sec>02d.%<micro>06d +0000',
         year: time.year, mon: time.month, day: time.day,
         hour: time.hour, min: time.min, sec: time.sec, micro: micro)
end

def format_timezone_offset(timezone)

def format_timezone_offset(timezone)
  sign = timezone.negative? ? '-' : '+'
  offset = timezone.abs
  tzhour = offset / 3600
  tzmin = (offset % 3600) / 60
  format('%<sign>s%<hour>02d:%<min>02d', sign: sign, hour: tzhour, min: tzmin)
end

def integer_to_hugeint(value)

def integer_to_hugeint(value)
  case value
  when Integer
    [_hugeint_lower(value), _hugeint_upper(value)]
  else
    raise(ArgumentError, "The argument `#{value.inspect}` must be Integer.")
  end
end