require "chamber"

# FIXME: files named with hyphens will not be found by Chamber for now
# FIXME: reloading of configuration from API

class ConfigMissingParameter    < StandardError; end
class ConfigOtherError          < StandardError; end
class ConfigParseError          < StandardError; end
class ConfigMultipleGemspec     < StandardError; end
class ConfigMissingGemspec      < StandardError; end

class Conf
  extend Chamber

  class << self
    attr_accessor :app_env

    attr_reader :app_root
    attr_reader :app_libs

    attr_reader :app_name
    attr_reader :app_ver
    attr_reader :app_started

    attr_reader :spec
    attr_reader :files
    attr_reader :host
    attr_reader :pidfile

  end

  def self.init app_root = nil
    # Defaults, hostname
    @files    = []
    @app_env  = "production"
    @app_started  = Time.now
    @host     = `hostname`.to_s.chomp.split(".").first

    # Grab app root
    @app_root = File.expand_path( File.dirname(__FILE__) + "/../../")
    @app_libs = File.expand_path( File.dirname(__FILE__) )

    # Try to find any gemspec file
    matches   = Dir["#{@app_root}/*.gemspec"]
    fail ConfigMissingGemspec, "gemspec file not found: #{gemspec_path}" if matches.size < 1
    fail ConfigMultipleGemspec, "gemspec file not found: #{gemspec_path}" if matches.size > 1

    # Load Gemspec (just the only match)
    @spec     = Gem::Specification::load(matches.first)
    @app_name = @spec.name
    @app_ver  = @spec.version
    fail ConfigMissingParameter, "gemspec: missing name" unless @app_name
    fail ConfigMissingParameter, "gemspec: missing version" unless @app_ver

    # Add config files
    add_default_config
    add_etc_config
  end

  def self.prepare args = {}
    # Add extra config file
    add_extra_config args[:config]

    # Load configuration files
    load_files

    # Init New Relic
    prepare_newrelic self[:newrelic], self.at(:logs, :newrelic)

    # Prepare PID file is not present
    prepare_pidfile

    # Try to access any key to force parsing of the files
    self[:dummy]

  rescue Psych::SyntaxError => e
    fail ConfigParseError, e.message
  rescue StandardError => e
    fail ConfigOtherError, "#{e.message} \n #{e.backtrace.to_yaml}"
  end

  def self.reload!
    load_files
  end

  def self.dump
    to_hash.to_yaml(indent: 4, useheader: true, useversion: false )
  end

  # Direct access to any depth
  def self.at *path
    path.reduce(Conf) { |m, key| m && m[key.to_s] }
  end

  def self.newrelic_enabled?
    !!self[:newrelic]
  end

protected

  def self.load_files
    load files: @files, namespaces: { environment: @app_env }
  end

  def self.add_default_config
    @files << "#{@app_root}/defaults.yml" if @app_root
  end

  def self.add_etc_config
    @files << File.expand_path("/etc/#{@app_name}.yml") if @app_name
  end

  def self.add_extra_config path
    @files << File.expand_path(path) if path
  end

  def self.prepare_pidfile
    self[:pidfile] ||= "/tmp/#{@app_name}-#{@host}-#{self[:port]}.pid"
  end

  def self.prepare_newrelic section, logfile
    # Disable NewRelic if no config present
    unless section.is_a?(Hash)
      ENV["NEWRELIC_AGENT_ENABLED"] = "false"
      return
    end

    # Enable GC profiler
    GC::Profiler.enable

    # Enable module
    ENV["NEWRELIC_AGENT_ENABLED"] = "true"
    ENV["NEW_RELIC_MONITOR_MODE"] = "true"

    # License
    ENV["NEW_RELIC_LICENSE_KEY"] = section[:licence].to_s

    # Appname
    platform = section[:platform] || self.host
    section[:app_name] ||= "#{@app_name}-#{platform}-#{@app_env}"
    ENV["NEW_RELIC_APP_NAME"] = section[:app_name].to_s

    # Logfile
    ENV["NEW_RELIC_LOG"] = logfile.to_s if logfile
  end

end