class Setting < ApplicationRecord
  encrypts :encrypted_value

  validates :key, presence: true, uniqueness: true

  # Default values for all application settings. New settings should be added
  # here so that ensure_defaults_exist creates them on existing installations
  # (called automatically when an admin visits the settings page).
  # The view (app/views/admin/settings.html.erb) is hardcoded — update it too.
  DEFAULTS = {
    "public_registration" => true,
    "update_prerelease" => false,
    "oidc_issuer" => "",
    "oidc_client_id" => "",
    "oidc_client_secret" => nil  # secret type — stored encrypted
  }.freeze

  def self.defined_keys
    DEFAULTS.keys
  end

  def self.ensure_defaults_exist
    DEFAULTS.each do |key, default|
      next if exists?(key: key)
      if default.nil? && key.end_with?("_secret")
        create!(key: key, value_type: "secret")
      else
        self[key] = default
      end
    end
  end

  # Getter for type casting
  def typed_value
    case value_type
    when "integer" then value.to_i
    when "boolean" then value == "true"
    when "array", "hash" then JSON.parse(value)
    when "secret" then encrypted_value
    when "string" then value # Default for string
    else value # Fallback if value_type is nil or unknown
    end
  rescue JSON::ParserError
    value_type == "array" ? [] : {}
  end

  # Setter for type casting and setting the string value_type
  def typed_value=(val)
    if value_type == "secret"
      self.encrypted_value = val.to_s
      self.value = nil
      return
    end

    self.value_type = case val
    when Integer then "integer"
    when TrueClass, FalseClass then "boolean"
    when Array then "array"
    when Hash then "hash"
    else "string" # Default for unknown types
    end
    self.value = val.is_a?(Array) || val.is_a?(Hash) ? val.to_json : val.to_s
  end

  # Helper methods for easy access (remain unchanged)
  def self.[](key)
    setting = find_by(key: key)
    # Never cache secret settings — the cache may store values in cleartext.
    return setting&.typed_value if setting&.value_type == "secret"

    Rails.cache.fetch("settings/#{key}") { setting&.typed_value }
  end

  def self.[]=(key, val)
    setting = find_or_initialize_by(key: key)
    # If we already have a value_type, try to convert the incoming value accordingly
    if setting.persisted? && setting.value_type
      val = case setting.value_type
      when "integer" then val.to_i
      when "boolean" then val.to_s == "true"
      when "secret" then val.to_s
      when "array" then
        begin
          JSON.parse(val.is_a?(String) ? val : val.to_json)
        rescue JSON::ParserError
          []
        end
      when "hash" then
        begin
          JSON.parse(val.is_a?(String) ? val : val.to_json)
        rescue JSON::ParserError
          {}
        end
      else val
      end
    end
    setting.typed_value = val
    setting.save
  end

  # Cache invalidation
  after_commit :clear_cache

  private

  def clear_cache
    Rails.cache.delete("settings/#{key}")
  end
end
