# frozen_string_literal: true

class ThemeSettingsManager
  attr_reader :name, :theme, :default

  def self.types
    ThemeSetting.types
  end

  def self.cast_row_value(row)
    type_name = self.types.invert[row.data_type].downcase.capitalize
    klass = "ThemeSettingsManager::#{type_name}".constantize
    klass.cast(klass.extract_value_from_row(row))
  end

  def self.create(name, default, type, theme, opts = {})
    type_name = self.types.invert[type].downcase.capitalize
    klass = "ThemeSettingsManager::#{type_name}".constantize
    klass.new(name, default, theme, opts)
  end

  def self.cast(value)
    value
  end

  def self.extract_value_from_row(row)
    row.value
  end

  def initialize(name, default, theme, opts = {})
    @name = name.to_sym
    @default = default
    @theme = theme
    @opts = opts
    @types = self.class.types
  end

  def value
    has_record? ? db_record.value : default
  end

  def type_name
    self.class.name.demodulize.downcase.to_sym
  end

  def type
    @types[type_name]
  end

  def description
    @opts[:description] # Old method of specifying description. Is now overridden by locale file
  end

  def requests_refresh?
    @opts[:refresh]
  end

  def value=(new_value)
    ensure_is_valid_value!(new_value)
    value = new_value.to_s

    record = has_record? ? update_record!(value:) : create_record!(value:)

    record.value
  end

  def db_record
    # theme.theme_settings will already be preloaded, so it is better to use
    # `find` on an array, rather than make a round trip to the database
    theme.theme_settings.to_a.find do |i|
      i.name.to_s == @name.to_s && i.data_type.to_s == type.to_s
    end
  end

  def update_record!(args)
    db_record.tap { |instance| instance.update!(args) }
  end

  def create_record!(args)
    record = ThemeSetting.new(name: @name, data_type: type, theme: @theme, **args)
    record.save!
    record
  end

  def has_record?
    db_record.present?
  end

  def ensure_is_valid_value!(new_value)
    return if new_value.nil?

    error_messages = ThemeSettingsValidator.validate_value(new_value, type, @opts)
    raise Discourse::InvalidParameters.new error_messages.join(" ") if error_messages.present?
  end

  def has_min?
    min = @opts[:min]
    (min.is_a?(::Integer) || min.is_a?(::Float)) && min != -::Float::INFINITY
  end

  def has_max?
    max = @opts[:max]
    (max.is_a?(::Integer) || max.is_a?(::Float)) && max != ::Float::INFINITY
  end
end