class Fluentd
  include ActiveModel::Model
  include ActiveModel::Validations::Callbacks

  validates :variant, inclusion: { in: proc { Fluentd.variants } }
  validates :log_file, presence: true
  validates :pid_file, presence: true
  validates :config_file, presence: true
  validate :validate_permissions

  before_validation :expand_paths

  COLUMNS = [:id, :variant, :log_file, :pid_file, :config_file, :note]
  DEFAULT_CONF = <<-CONF.strip_heredoc
    <source>
      # http://docs.fluentd.org/articles/in_forward
      @type forward
      port 24224
    </source>

    <source>
      # http://docs.fluentd.org/articles/in_http
      @type http
      port 9880
    </source>

    <source>
      @type monitor_agent
      port 24220
    </source>
    <source>
      @type debug_agent
      port 24230
    </source>

    <match debug.*>
      # http://docs.fluentd.org/articles/out_stdout
      @type stdout
    </match>
  CONF

  attr_accessor(*COLUMNS)

  def self.variants
    %w(fluentd_gem td-agent)
  end

  def self.json_path
    FluentdUI.data_dir + "/#{Rails.env}-fluentd.json"
  end

  def fluentd?
    variant == "fluentd_gem"
  end
  alias :fluentd_gem? :fluentd?

  def td_agent?
    variant == "td-agent"
  end

  def agent
    klass = variant.underscore.camelize
    Agent.const_get(klass).new({
      :pid_file => pid_file,
      :log_file => log_file,
      :config_file => config_file,
    })
  end

  def load_settings_from_agent_default
    agent.class.default_options.each_pair do |key, value|
      send("#{key}=", value)
    end
  end

  def label
    "fluentd" # NOTE: for multiple fluentd management, but only single fluentd manage now
  end

  def expand_paths
    %w(pid_file log_file config_file).each do |column|
      path = send(column)
      next if path.blank?
      self.send("#{column}=", File.expand_path(path))
    end
  end

  def validate_permissions
    %w(pid_file log_file config_file).each do |column|
      check_permission(column)
    end
  end

  def check_permission(column)
    path = send(column)
    return if path.blank? # if empty, presence: true will catch it

    begin
      FileUtils.mkdir_p(File.dirname(path))
    rescue Errno::EACCES
      errors.add(column, :lack_write_permission)
      return
    end

    if File.exist?(path)
      if File.directory?(path)
        errors.add(column, :is_a_directory)
      end

      unless File.writable?(path)
        errors.add(column, :lack_write_permission)
      end
      unless File.readable?(path)
        errors.add(column, :lack_read_permission)
      end
    else
      unless File.writable?(File.dirname(path))
        errors.add(column, :lack_write_permission)
      end
    end
  end

  def ensure_default_config_file
    return true if File.size?(config_file)

    File.open(config_file, "w") do |f|
      f.write DEFAULT_CONF
    end
  end


  # ActiveRecord mimic

  def self.instance
    return unless exists?
    attr = JSON.parse(File.read(json_path))
    Fluentd.new(attr)
  end

  def self.exists?
    File.exists?(json_path)
  end

  def update_attributes(params)
    params.each_pair do |k, v|
      send("#{k}=", v)
    end
  end

  def save
    return false unless valid?
    json = COLUMNS.inject({}) do |result, col|
      result[col] = send(col)
      result
    end.to_json
    File.open(self.class.json_path, "w") do |f|
      f.write json
    end && ensure_default_config_file
  end

  def destroy
    File.unlink(self.class.json_path)
  end
end
