module JamRuby

  # describes what users have rights to which tracks
  class JamTrackMixdownPackage < ActiveRecord::Base
    include JamRuby::S3ManagerMixin

    @@log = Logging.logger[JamTrackMixdownPackage]

    # these are used as extensions for the files stored in s3
    FILE_TYPE_MP3 = 'mp3'
    FILE_TYPE_OGG = 'ogg'
    FILE_TYPE_AAC = 'aac'
    FILE_TYPES = [FILE_TYPE_MP3, FILE_TYPE_OGG, FILE_TYPE_AAC]

    SAMPLE_RATE_44 = 44
    SAMPLE_RATE_48 = 48
    SAMPLE_RATES = [SAMPLE_RATE_44, SAMPLE_RATE_48]

    ENCRYPT_TYPE_JKZ = 'jkz'
    ENCRYPT_TYPES = [ENCRYPT_TYPE_JKZ, nil]

    default_scope { order('created_at desc') }

    belongs_to :jam_track_mixdown, class_name: "JamRuby::JamTrackMixdown", dependent: :destroy

    validates :jam_track_mixdown, presence: true

    validates :file_type, inclusion: {in: FILE_TYPES}
    validates :sample_rate, inclusion: {in: SAMPLE_RATES}
    validates :encrypt_type, inclusion: {in: ENCRYPT_TYPES}
    validates_uniqueness_of :file_type, scope: [:sample_rate, :encrypt_type, :jam_track_mixdown_id]
    validates :signing, inclusion: {in: [true, false]}
    validates :signed, inclusion: {in: [true, false]}

    validate :verify_download_count
    before_destroy :delete_s3_files
    after_save :after_save

    MAX_JAM_TRACK_DOWNLOADS = 1000

    def self.estimated_queue_time
      jam_track_signing_count = JamTrackRight.where(queued: true).count

      #mixdowns = JamTrackMixdownPackage.unscoped.select('count(CASE WHEN queued THEN 1 ELSE NULL END) as queue_count, count(CASE WHEN speed_pitched THEN 1 ELSE NULL END) as speed_pitch_count').where(queued: true).limit(1)
      mixdowns = ActiveRecord::Base.connection.execute("select count(CASE WHEN queued THEN 1 ELSE NULL END) as queue_count, count(CASE WHEN speed_pitched THEN 1 ELSE NULL END) as speed_pitch_count FROM jam_track_mixdown_packages WHERE queued = true")[0]
      total_mixdowns = mixdowns['queue_count'].to_i
      slow_mixdowns = mixdowns['speed_pitch_count'].to_i
      fast_mixdowns = total_mixdowns - slow_mixdowns

      guess = APP_CONFIG.estimated_jam_track_time * jam_track_signing_count + APP_CONFIG.estimated_fast_mixdown_time * fast_mixdowns + APP_CONFIG.estimated_slow_mixdown_time * slow_mixdowns

      Stats.write('web.jam_track.queue_time', {value: guess / 60.0, jam_tracks: jam_track_signing_count, slow_mixdowns: slow_mixdowns, fast_mixdowns: fast_mixdowns})
      guess
    end

    def after_save
      # try to catch major transitions:

      # if just queue time changes, start time changes, or signed time changes, send out a notice
      if signing_queued_at_was != signing_queued_at || signing_started_at_was != signing_started_at || last_signed_at_was != last_signed_at || current_packaging_step != current_packaging_step_was || packaging_steps != packaging_steps_was
        SubscriptionMessage.mixdown_signing_job_change(self)
      end
    end

    def self.create(mixdown, file_type, sample_rate, encrypt_type)

      package = JamTrackMixdownPackage.new
      package.speed_pitched = mixdown.will_pitch_shift?
      package.jam_track_mixdown = mixdown
      package.file_type = file_type
      package.sample_rate = sample_rate
      package.signed = false
      package.signing = false
      package.encrypt_type = encrypt_type
      package.save
      package
    end

    def verify_download_count
      if (self.download_count < 0 || self.download_count > MAX_JAM_TRACK_DOWNLOADS) && !@current_user.admin
        errors.add(:download_count, "must be less than or equal to #{MAX_JAM_TRACK_DOWNLOADS}")
      end
    end

    def is_pitch_speed_shifted?
      mix_settings = JSON.parse(self.settings)
      mix_settings["speed"] || mix_settings["pitch"]
    end

    def finish_errored(error_reason, error_detail)
      self.last_errored_at = Time.now
      self.last_signed_at = Time.now
      self.error_count = self.error_count + 1
      self.error_reason = error_reason
      self.error_detail = error_detail
      self.should_retry = self.error_count < 5
      self.signing = false
      self.signing_queued_at = nil # if left set, throws off signing_state on subsequent signing attempts

      if save
        Notification.send_mixdown_sign_failed(self)
      else
        raise "Error sending notification #{self.errors}"
      end
    end

    def finish_sign(url, private_key, length, md5)
      self.url = url
      self.private_key = private_key
      self.signing_queued_at = nil # if left set, throws off signing_state on subsequent signing attempts
      self.downloaded_since_sign = false
      self.last_signed_at = Time.now
      self.length = length
      self.md5 = md5
      self.signed = true
      self.signing = false
      self.error_count = 0
      self.error_reason = nil
      self.error_detail = nil
      self.should_retry = false
      save!
    end

    def store_dir
      "jam_track_mixdowns/#{created_at.strftime('%m-%d-%Y')}/#{self.jam_track_mixdown.user_id}"
    end

    def filename
      if encrypt_type
        "#{id}.#{encrypt_type}"
      else
        "#{id}.#{file_type}"
      end
    end


    # creates a short-lived URL that has access to the object.
    # the idea is that this is used when a user who has the rights to this tries to download this JamTrack
    # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
    # but the url is short lived enough so that it wouldn't be easily shared
    def sign_url(expiration_time = 120, content_type = nil, response_content_disposition = nil)
      options = {:expires => expiration_time, :secure => true}
      options[:response_content_type] = content_type if content_type
      options[:response_content_disposition] = response_content_disposition if response_content_disposition
      s3_manager.sign_url(self['url'], options)
    end


    def enqueue
      begin
        self.signing_queued_at = Time.now
        self.signing_started_at = nil
        self.last_signed_at = nil
        self.queued = true
        self.save

        queue_time = JamTrackMixdownPackage.estimated_queue_time

        # is_pitch_speed_shifted?
        Resque.enqueue(JamTrackMixdownPackager, self.id)
        return queue_time
      rescue Exception => e
        puts "e: #{e}"
        # implies redis is down. we don't update started_at by bailing out here
        false
      end
    end

    # if the job is already signed, just queued up for signing, or currently signing, then don't enqueue... otherwise fire it off
    def enqueue_if_needed
      state = signing_state
      if state == 'SIGNED' || state == 'SIGNING' || state == 'QUEUED'
        false
      else
        return enqueue
      end
    end

    def ready?
      self.signed && self.url.present?
    end

    # returns easy to digest state field
    # SIGNED - the package is ready to be downloaded
    # ERROR - the package was built unsuccessfully
    # SIGNING_TIMEOUT - the package was kicked off to be signed, but it seems to have hung
    # SIGNING - the package is currently signing
    # QUEUED_TIMEOUT - the package signing job (JamTrackBuilder) was queued, but never executed
    # QUEUED - the package is queued to sign
    # QUIET - the jam_track_right exists, but no job has been kicked off; a job needs to be enqueued
    def signing_state
      state = nil

      if signed
        state = 'SIGNED'
      elsif signing_started_at && signing
        # the maximum amount of time the packaging job can take is 10 seconds * num steps.  For a 10 track song, this will be 110 seconds. It's a bit long.
        if Time.now - signing_started_at > APP_CONFIG.signing_job_signing_max_time
          state = 'SIGNING_TIMEOUT'
        elsif Time.now - last_step_at > APP_CONFIG.mixdown_step_max_time
          state = 'SIGNING_TIMEOUT'
        else
          state = 'SIGNING'
        end
      elsif signing_queued_at
        if Time.now - signing_queued_at > APP_CONFIG.mixdown_job_queue_max_time
          state = 'QUEUED_TIMEOUT'
        else
          state = 'QUEUED'
        end
      elsif error_count > 0
        state = 'ERROR'
      else
        if Time.now - created_at > 60 # it should not take more than a minute to get QUIET out
          state = 'QUIET_TIMEOUT'
        else
          state = 'QUIET' # needs to be poked to go build
        end

      end
      state
    end

    def signed?
      signed
    end

    def update_download_count(count=1)
      self.download_count = self.download_count + count
      self.last_downloaded_at = Time.now

      if self.signed
        self.downloaded_since_sign = true
      end
    end


    def self.stats
      stats = {}

      result = JamTrackMixdownPackage.unscoped.select('count(id) as total, count(CASE WHEN signing THEN 1 ELSE NULL END) as signing_count')

      stats['count'] = result[0]['total'].to_i
      stats['signing_count'] = result[0]['signing_count'].to_i
      stats
    end


    def delete_s3_files
      s3_manager.delete(self.url) if self.url && s3_manager.exists?(self.url)
    end

  end
end

