module CamaleonCms
  module Metas
    extend ActiveSupport::Concern

    included do
      # options and metas auto save support
      attr_accessor :data_options
      attr_accessor :data_metas

      after_create  :save_metas_options, unless: :save_metas_options_skip
      before_update :fix_save_metas_options_no_changed
    end

    # Add meta with value or Update meta with key: key
    # return true or false
    def set_meta(key, value)
      metas.where(key: key).update_or_create({ value: fix_meta_value(value) })
      cama_set_cache("meta_#{key}", value)
    end

    # return value of meta with key: key,
    # if meta not exist, or its value == "", return default
    def get_meta(key, default = nil)
      key_str = key.is_a?(Symbol) ? key.to_s : key
      cama_fetch_cache("meta_#{key_str}") do
        option = metas.loaded? ? metas.select { |m| m.key == key }.first : metas.where(key: key_str).first
        res = ''
        if option.present?
          value = begin
            JSON.parse(option.value)
          rescue StandardError
            option.value
          end
          res = begin
            (value.is_a?(Hash) ? value.with_indifferent_access : value)
          rescue StandardError
            option.value
          end
        end
        res == '' ? default : res
      end
    end

    # delete meta
    def delete_meta(key)
      metas.where(key: key).destroy_all
      cama_remove_cache("meta_#{key}")
    end

    # return configurations for current object, sample: {"type":"post_type","object_id":"127"}
    def options(meta_key = '_default')
      get_meta(meta_key, {})
    end
    alias cama_options options

    # add configuration for current object
    # key: attribute name
    # value: attribute value
    # meta_key: (String) name of the meta attribute
    # sample: mymodel.set_custom_option("my_settings", "color", "red")
    def set_option(key, value = nil, meta_key = '_default')
      return if key.nil?

      data = cama_options(meta_key)
      data[key] = fix_meta_var(value)
      set_meta(meta_key, data)
      value
    end

    # return configuration for current object
    # key: attribute name
    # default: if the attribute doesn't exist, or its value == "", return default
    # return value for attribute
    def get_option(key = nil, default = nil, meta_key = '_default')
      values = cama_options(meta_key)
      key = key.to_sym
      values.key?(key) && values[key] != '' ? values[key] : default
    end

    # delete attribute from configuration
    def delete_option(key, meta_key = '_default')
      values = cama_options(meta_key)
      key = key.to_sym
      values.delete(key) if values.key?(key)
      set_meta(meta_key, values)
    end

    # set multiple configurations
    # h: {ket1: "sdsds", ff: "fdfdfdfd"}
    def set_options(h = {}, meta_key = '_default')
      return unless h.present?

      data = cama_options(meta_key)
      PluginRoutes.fixActionParameter(h).to_sym.each do |key, value|
        data[key] = fix_meta_var(value)
      end
      set_meta(meta_key, data)
    end
    alias set_multiple_options set_options

    # save multiple metas
    # sample: set_metas({name: 'Owen', email: 'owenperedo@gmail.com'})
    def set_metas(data_metas)
      (data_metas.nil? ? {} : data_metas).each do |key, value|
        set_meta(key, value)
      end
    end

    # permit to skip save_metas_options in specific models
    def save_metas_options_skip
      false
    end

    # fix to save options and metas when a model was not changed
    def fix_save_metas_options_no_changed
      save_metas_options # unless self.changed?
    end

    # save all settings for this post type received in data_options and data_metas attribute (options and metas)
    # sample: Site.first.post_types.create({name: "owen", slug: "my_post_type", data_options: { has_category: true, default_layout: "my_layout" }})
    def save_metas_options
      set_multiple_options(data_options)
      return unless data_metas.present?

      data_metas.each do |key, val|
        set_meta(key, val)
      end
    end

    private

    # fix to parse value
    def fix_meta_value(value)
      changed_value = if value.is_a?(ActionController::Parameters)
                        value.to_json
                      elsif value.is_a?(Array) || value.is_a?(Hash)
                        JSON.generate(value)
                      else
                        value
                      end
      fix_meta_var(changed_value)
    end

    # fix to detect type of the variable
    def fix_meta_var(value)
      value = value.to_var if value.is_a?(String)
      value
    end
  end
end
