require 'json'
require 'resque'

require 'resque-lonely_job'
require 'net/http'
require 'digest/md5'

module JamRuby

  # executes a mix of tracks, creating a final output mix
  class IcecastConfigWriter
    extend Resque::Plugins::JamLonelyJob

    @@log = Logging.logger[IcecastConfigWriter]

    attr_accessor :icecast_server_id

    def self.queue
      queue_name(::APP_CONFIG.icecast_server_id)
    end

    def self.queue_jobs_needing_retry
      # if we haven't seen updated_at be tickled in 5 minutes, but config_changed is still set to TRUE, this record has gotten stale
      IcecastServer.where("config_changed = 1 AND updated_at < (NOW() - interval '#{APP_CONFIG.icecast_max_missing_check} second')").find_each(:batch_size => 100) do |server|
        IcecastConfigWriter.enqueue(server.server_id)
      end
    end

    def self.queue_name(server_id)
      "icecast-#{server_id}"
    end

    def self.lock_timeout
      # this should be enough time to make sure the job has finished, but not so long that the system isn't recovering from a abandoned job
      60
    end

    def self.perform(icecast_server_id)
        icecast = IcecastConfigWriter.new()
        icecast.icecast_server_id = icecast_server_id
        icecast.run
    end

    def self.enqueue(server_id)
      begin
        Resque.enqueue_to(queue_name(server_id), IcecastConfigWriter, server_id)
        return true
      rescue
        @@log.error("unable to enqueue IceastConfigWriter(#{server_id}). #{$!}")
        # implies redis is down
        return false
      end
    end

    def initialize

    end

    def validate
      raise "icecast_server_id not spceified" unless icecast_server_id
      raise "queue routing mismatch error. requested icecast_server_id: #{icecast_server_id}, configured icecast_server_id: #{APP_CONFIG.icecast_server_id}" unless icecast_server_id == APP_CONFIG.icecast_server_id
    end

    def execute(cmd)
      system cmd
      $?.exitstatus
    end

    def reload
      cmd = APP_CONFIG.icecast_reload_cmd
      result = execute(cmd)
      raise "unable to execute icecast reload cmd=#{cmd}. result=#{$?}" unless result == 0

      sleep APP_CONFIG.icecast_wait_after_reload
    end

    def run
      validate

      config_file = APP_CONFIG.icecast_config_file

      # check if the config file is there at all; if it's not, we need to generate it regardless if config has changed
      query = {server_id: icecast_server_id}

      icecast_server = IcecastServer.where(server_id: icecast_server_id).first

      raise "can not find icecast server with query #{query}" unless icecast_server

      if File.exist?(config_file) && !icecast_server.config_changed
        @@log.info("config not changed. skipping run for server: #{icecast_server.server_id}")
      else
        # don't try to write to the file if for some reason the model isn't valid
        # this could happen if an admin mucks around in the db directly
        raise "icecast_server.id=#{icecast_server.server_id} not valid. errors=#{icecast_server.errors.inspect}" unless icecast_server.valid?

        # write the new config to a temporary location
        tmp_config  = Dir::Tmpname.make_tmpname(["#{Dir.tmpdir}/icecast", '.xml'], nil)

        buffer = nil
        # allow no write to the server while dumping XML

        icecast_server.with_lock do
          buffer = StringIO.new
          icecast_server.dumpXml(buffer)
        end

        buffer.rewind
        File.open(tmp_config, 'w') do |f|
          f.write buffer.read
        end

        # if written successfully, overwrite the current file
        FileUtils.mv tmp_config, config_file

        # reload server
        reload

        icecast_server.config_updated
      end

      @@log.info("successful update of config for server: #{icecast_server.server_id}")

    end
  end
end