# -*- coding: utf-8 -*-
module JamRuby
  class JamTrack < ActiveRecord::Base
    include JamRuby::S3ManagerMixin

    TIME_SIGNATURES = %w{4/4 3/4 2/4 6/8 5/8'}
    STATUS = %w{Staging Production Retired}
    RECORDING_TYPE = %w{Cover Original}
    PRO = %w{ASCAP BMI SESAC}
    SALES_REGION = ['United States', 'Worldwide']

    PRODUCT_TYPE = 'JamTrack'

    @@log = Logging.logger[JamTrack]

    attr_accessor   :uploading_preview
    attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type,
                  :original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genres_jam_tracks_attributes, :sales_region, :price,
                  :reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
                  :licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes,
                  :jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration,
                    :server_fixation_date, :hfa_license_status, :hfa_license_desired, :alternative_license_status, :hfa_license_number, :hfa_song_code, :album_title, :year, :allow_free, as: :admin

    validates :name,            presence: true, length: {maximum: 200}
    validates :plan_code,       presence: true, uniqueness: true, length: {maximum: 50  }
    validates :description,     length: {maximum: 1000}
    validates :time_signature,  inclusion: {in: [nil] + [''] + TIME_SIGNATURES} # the empty string is needed because of activeadmin
    validates :status,          inclusion: {in: [nil] + STATUS}
    validates :recording_type,  inclusion: {in: [nil] + RECORDING_TYPE}
    validates :original_artist, length: {maximum: 200}
    validates :songwriter,      length: {maximum: 1000}
    validates :publisher,       length: {maximum: 1000}
    validates :sales_region,    inclusion: {in: [nil] + SALES_REGION}
    validates_format_of :price, with: /\A\d+\.*\d{0,2}\z/
    validates :version,         presence: true
    validates :pro_ascap,       inclusion: {in: [true, false]}
    validates :pro_bmi,         inclusion: {in: [true, false]}
    validates :pro_sesac,       inclusion: {in: [true, false]}
    validates :public_performance_royalty,            inclusion: {in: [nil, true, false]}
    validates :reproduction_royalty,                  inclusion: {in: [nil, true, false]}
    validates :public_performance_royalty,            inclusion: {in: [nil, true, false]}
    validates :duration,                              numericality: {only_integer: true}, :allow_nil => true
    validates :hfa_license_status,                    inclusion: {in: [true, false]}
    validates :hfa_license_desired,                   inclusion: {in: [true, false]}
    validates :alternative_license_status,            inclusion: {in: [true, false]}
    validates :hfa_license_number,                    numericality: {only_integer: true}, :allow_nil => true
    validates :hfa_song_code,                         length: {maximum: 200}
    validates :album_title,                           length: {maximum: 200}
    validates :slug,            uniqueness: true

    validates_format_of :reproduction_royalty_amount, with: /\A\d+\.*\d{0,4}\z/, :allow_blank => true
    validates_format_of :licensor_royalty_amount,     with: /\A\d+\.*\d{0,4}\z/, :allow_blank => true

    belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id', :inverse_of => :jam_tracks

    has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id", inverse_of: :jam_track
    has_many :genres, :through => :genres_jam_tracks, :class_name => "JamRuby::Genre", :source => :genre

    has_many :jam_track_tracks, -> { order('track_type ASC, position ASC, part ASC, instrument_id ASC' )},:class_name => "JamRuby::JamTrackTrack"
    has_many :jam_track_tap_ins, -> { order('offset_time ASC')}, :class_name => "JamRuby::JamTrackTapIn"
    has_many :jam_track_files, :class_name => "JamRuby::JamTrackFile"

    has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight"  #, inverse_of: 'jam_track', :foreign_key => "jam_track_id"  # '

    has_many :owners, :through => :jam_track_rights, :class_name => "JamRuby::User", :source => :user

    has_many    :playing_sessions, :class_name => "JamRuby::ActiveMusicSession", :dependent => :destroy

    has_many :recordings, :class_name => "JamRuby::Recording", :dependent => :destroy

    # VRFS-2916 jam_tracks.id is varchar: REMOVE
    # has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => :jam_track_id, :dependent => :destroy
    # VRFS-2916 jam_tracks.id is varchar: ADD
     has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy

    has_many :jam_track_session, :class_name => "JamRuby::JamTrackSession"

    # when we know what JamTrack this refund is related to, these are associated
    belongs_to :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook'

    accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true
    accepts_nested_attributes_for :jam_track_tap_ins, allow_destroy: true


    # we can make sure a few things stay in sync here.
    # 1) the reproduction_royalty_amount has to stay in sync based on duration
    # 2) the onboarding_exceptions JSON column
    after_save :sync_reproduction_royalty
    after_save :sync_onboarding_exceptions

    def increment_version!
      self.version = version.to_i + 1
      save!
    end

    def sync_reproduction_royalty

      # reproduction royalty table based on duration

      # The statutory mechanical royalty rate for permanent digital downloads is:
      # 9.10¢ per copy for songs 5 minutes or less, or
      # 1.75¢ per minute or fraction thereof, per copy for songs over 5 minutes.
      # So the base rate is 9.1 cents for anything up to 5 minutes.
      # 5.01 to 6 minutes should be 10.5 cents.
      # 6.01 to 7 minutes should be 12.25 cents.
      # Etc.

      royalty = nil
      if self.duration
        minutes = (self.duration - 1) / 60
        extra_minutes = minutes - 4
        extra_minutes = 0 if extra_minutes < 0
        royalty = (0.091 + (0.0175 * extra_minutes)).round(5)
      end
      self.update_column(:reproduction_royalty_amount, royalty)

      true
    end

    def sync_onboarding_exceptions

      exceptions = {}
      if self.duration.nil?
        exceptions[:no_duration] = true
      end

      if self.genres.count == 0
        exceptions[:no_genres] = true
      end

      if self.year.nil?
        exceptions[:no_year] = true
      end

      if self.licensor.nil?
        exceptions[:no_licensor] = true
      end

      if self.missing_instrument_info?
        exceptions[:unknown_instrument] = true
      end

      if self.master_track.nil?
        exceptions[:no_master] = true
      end

      if missing_previews?
        exceptions[:missing_previews] = true
      end

      if duplicate_positions?
        exceptions[:duplicate_positions] = true
      end

      if exceptions.keys.length == 0
        self.update_column(:onboarding_exceptions, nil)
      else
        self.update_column(:onboarding_exceptions, exceptions)
      end

      true
    end

    def sale_display(variant = nil)
      if variant == ShoppingCart::JAMTRACK_FULL
        variant_desc = 'FULL'
      elsif variant == ShoppingCart::JAMTRACK_DOWNLOAD
        variant_desc = 'UPRGADE'
      elsif variant == ShoppingCart::JAMTRACK_STREAM
        variant_desc = 'FOR USE ONLY WITHIN APP'
      else
        variant_desc = 'UNKNOWN'
      end


      "JamTrack: #{name} - #{variant_desc}"
    end


    def duplicate_positions?
      counter = {}
      jam_track_tracks.each do |track|
        count = counter[track.position]
        if count.nil?
          count = 0
        end
        counter[track.position]  = count + 1
      end

      duplicate = false
      counter.each do|position, count|
        if count > 1
          duplicate = true
          break
        end
      end
      duplicate
    end

    def missing_instrument_info?
      missing_instrument_info = false
      self.jam_track_tracks.each do |track|
        if track.instrument_id == 'other' && (track.part == nil || track.part.start_with?('Other'))
          missing_instrument_info = true
          break
        end
      end
      missing_instrument_info
    end

    def missing_previews?
      missing_preview = false
      self.jam_track_tracks.each do |track|
        unless track.has_preview?
          missing_preview = true
          break
        end
      end
      missing_preview
    end

    def onboard_warnings
      warnings = []
      warnings << 'POSITIONS' if duplicate_positions?
      warnings << 'PREVIEWS'if missing_previews?
      warnings << 'DURATION' if duration.nil?
      warnings << 'JMEP' if jmep_json.blank?
      warnings.join(',')
    end

    def band_jam_track_count
      JamTrack.where(original_artist: original_artist).count
    end

    class << self 
      # @return array[artist_name(string)]
      def all_artists 
        JamTrack.select("original_artist").
                 group("original_artist").
                 order('original_artist').
                 collect{|jam_track|jam_track.original_artist}
      end

      # @return array[JamTrack] for given artist_name
      def tracks_for_artist(artist_name)
        JamTrack.where("original_artist=?", artist_name).all
      end

      # special case of index
      def autocomplete(options, user)

        if options[:match].blank?
          return {artists: [], songs: []}
        end

        options[:show_purchased_only] = options[:show_purchased_only]

        options[:limit] = options[:limit] || 5

        options[:artist_search] = options[:match]
        artists, pager = artist_index(options, user)

        options.delete(:artist_search)
        options[:song_search] = options[:match]
        options[:sort_by] = 'jamtrack'
        songs, pager = index(options, user)

        {artists: artists, songs:songs}
      end

      def purchase_stubs(user)
        JamTrack.
          select(['jam_tracks.id', :name, :original_artist, :year, 'jam_track_rights.created_at AS purchased_at']).
          joins(:jam_track_rights).
          where("jam_track_rights.user_id = ?", user.id).
          includes(:genres).
          order([:original_artist, :name])
      end

      def index(options, user)
        if options[:page]
          page = options[:page].to_i
          per_page = options[:per_page].to_i

          if per_page == 0
            # try and see if limit was specified
            limit = options[:limit]
            limit ||= 20
            limit = limit.to_i
            per_page = limit
          else
            limit = per_page
          end
 
          start = (page -1 )* per_page
        else
          limit = options[:limit]
          limit ||= 20
          limit = limit.to_i

          start = options[:start].presence
          start = start.to_i || 0

          page = 1 + start/limit
          per_page = limit
        end


        query = JamTrack.joins(:jam_track_tracks)
                    .paginate(page: page, per_page: per_page)

        if options[:show_purchased_only]
          query = query.joins(:jam_track_rights)
          query = query.where("jam_track_rights.user_id = ?", user.id)
        end

        if options[:search]
          tsquery = Search.create_tsquery(options[:search])
          if tsquery
              query = query.where("(search_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
          end
        end

        if options[:artist_search]
          tsquery = Search.create_tsquery(options[:artist_search])
          if tsquery
            query = query.where("(artist_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
          end
        end

        if options[:song_search]
          tsquery = Search.create_tsquery(options[:song_search])
          if tsquery
            query = query.where("(name_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
          end
        end

        if options[:artist].present?
          artist_param = options[:artist]
          # todo: add licensor option
          if artist_param == 'Stockton Helbing'
            licensor = JamTrackLicensor.find_by_name('Stockton Helbing')
            if licensor
              query = query.where(licensor_id: licensor.id)
            end
          else
            query = query.where("original_artist=?", options[:artist])
          end
        end

        if options[:song].present?
          query = query.where("name=?", options[:song])
        end

        if options[:id].present?
          query = query.where("jam_tracks.id=?", options[:id])
        end

        if options[:group_artist]
          query = query.select("original_artist, array_agg(jam_tracks.id) AS id, MIN(name) AS name, MIN(description) AS description, MIN(recording_type) AS recording_type, MIN(original_artist) AS original_artist, MIN(songwriter) AS songwriter, MIN(publisher) AS publisher, MIN(sales_region) AS sales_region, MIN(price) AS price, MIN(version) AS version")
          query = query.group("original_artist")
          query = query.order('jam_tracks.original_artist')
          query = query.includes([{ jam_track_tracks: :instrument }, { genres_jam_tracks: :genre }])
        else
          query = query.group("jam_tracks.id")
          if options[:sort_by] == 'jamtrack'
            query = query.order('jam_tracks.name')
          else
            query = query.order('jam_tracks.original_artist, jam_tracks.name')
          end


        end

        if (! user.try(:admin) && 'development' != Rails.env)
          query = query.where("jam_tracks.status = ?", 'Production') 
        end

        unless options[:genre].blank?
          query = query.joins(:genres)
          query = query.where('genre_id = ? ', options[:genre])
        end

        query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type = 'Track'") unless options[:instrument].blank?
        query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?

        # FIXME:  n+1 queries for rights and genres
        # query = query.includes([{ jam_track_tracks: :instrument }, 
        #                         :jam_track_tap_ins,
        #                         :jam_track_rights,
        #                         :genres])
                                # { genres_jam_tracks: :genre }, 
       # query = query.includes([{ jam_track_tracks: :instrument },
        #                        { genres_jam_tracks: :genre }])

        count = query.total_entries

        if count == 0
          [query, nil, count]
        elsif query.length < limit
          [query, nil, count]
        else
          [query, start + limit, count]
        end
      end


      # provides artist names and how many jamtracks are available for each
      def artist_index(options, user)
        if options[:page]
          page = options[:page].to_i
          per_page = options[:per_page].to_i

          if per_page == 0
            # try and see if limit was specified
            limit = options[:limit]
            limit ||= 100
            limit = limit.to_i
          else
            limit = per_page
          end

          start = (page -1 )* per_page
          limit = per_page
        else
          limit = options[:limit]
          limit ||= 100
          limit = limit.to_i

          start = options[:start].presence
          start = start.to_i || 0

          page = 1 + start/limit
          per_page = limit
        end


        query = JamTrack.paginate(page: page, per_page: per_page)
        query = query.select("original_artist, count(original_artist) AS song_count")
        query = query.group("original_artist")
        query = query.order('jam_tracks.original_artist')

        query = query.where("jam_tracks.status = ?", 'Production') unless user.admin

        if options[:show_purchased_only]
          query = query.joins(:jam_track_rights)
          query = query.where("jam_track_rights.user_id = ?", user.id)
        end

        if options[:artist_search]
          tsquery = Search.create_tsquery(options[:artist_search])
          if tsquery
            query = query.where("(artist_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
          end
        end


        unless options[:genre].blank?
          query = query.joins(:genres)
          query = query.where('genre_id = ? ', options[:genre])
        end

        query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank?
        query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?


        if query.length == 0
          [query, nil]
        elsif query.length < limit
          [query, nil]
        else
          [query, start + limit]
        end
      end
    end

    def click_track_file
      JamTrackFile.where(jam_track_id: self.id).where(file_type: 'ClickWav').first
    end

    def click_track
      JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Click').first
    end

    def has_count_in?
      has_count_in = false
      if jmep_json
        jmep = jmep_json

        if jmep["Events"]
          events = jmep["Events"]
          metronome = nil
          events.each do |event|
            if event.has_key?("metronome")
              metronome = event["metronome"]
              break
            end
          end
          if metronome
            has_count_in = true
          end
        end
      end

      has_count_in
    end

    def master_track
      JamTrackTrack.where(jam_track_id: self.id).where(track_type: 'Master').first
    end

    def stem_tracks
      JamTrackTrack.where(jam_track_id: self.id).where("track_type = 'Track' or track_type = 'Click'")
    end

    def can_download?(user)
      owners.include?(user)
    end

    def right_for_user(user, variant = nil)

      query = jam_track_rights.where("user_id=?", user)

      if variant
        if variant == ShoppingCart::JAMTRACK_DOWNLOAD
          query = query.where('can_download', true)
        elsif variant == ShoppingCart::JAMTRACK_FULL
          query = query.where('can_download', true)
        elsif variant == ShoppingCart::JAMTRACK_STREAM

        else
          throw 'unknown variant ' + variant
        end
      end
      query.first
    end


    def mixdowns_for_user(user)
      JamTrackMixdown.where(user_id:  user.id).where(jam_track_id: self.id)
    end

    def upgrade_price
      variant_price('download')
    end

    def variant_price(variant)
      if variant == 'full'
        download_price
      elsif variant == 'download'
        download_price - price
      else
        price
      end
    end

    def short_plan_code
      prefix = 'jamtrack-'
      plan_code[prefix.length..-1]
    end

    # http://stackoverflow.com/questions/4308377/ruby-post-title-to-slug
    def sluggarize(field)
      field.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
    end

    def generate_slug
      self.slug = sluggarize(original_artist) + '-' + sluggarize(name)

      if licensor && licensor.slug.present?
        #raise "no slug on licensor #{licensor.id}" if licensor.slug.nil?
        self.slug << "-" + licensor.slug
      end
    end

    def gen_plan_code
      # remove all non-alphanumeric chars from artist as well as name
      artist_code = original_artist.gsub(/[^0-9a-z]/i, '').downcase
      name_code = name.gsub(/[^0-9a-z]/i, '').downcase
      self.plan_code = "jamtrack-#{artist_code[0...20]}-#{name_code}"

      if licensor && licensor.slug
        raise "no slug on licensor #{licensor.id}" if licensor.slug.nil?
        self.plan_code << "-" + licensor.slug
      end

      self.plan_code = self.plan_code[0...50] # make sure it's a max of 50 long


    end

    def to_s
      "#{self.name} (#{self.original_artist})"
    end

    def self.latestPurchase(user_id)
      JamTrackRight
        .select('created_at')
        .where(user_id: user_id)
        .order('created_at DESC')
        .limit(1)
        .first
        .try(:created_at)
        .to_i
    end

    attr_accessor :preview_generate_error

    before_save :jmep_json_generate
    validate :jmep_text_validate

    def jmep_text_validate
      begin
        JmepManager.execute(self.jmep_text)
      rescue ArgumentError => err
        errors.add(:jmep_text, err.to_s)
      end
    end

    def jmep_json_generate
      self.licensor_id = nil if self.licensor_id == ''
      self.jmep_json = nil if self.jmep_json == ''
      self.time_signature = nil if self.time_signature == ''

      begin
        self[:jmep_json] = JmepManager.execute(self.jmep_text)
      rescue ArgumentError => err
        #errors.add(:jmep_text, err.to_s)
      end
    end

    # used in mobile simulate purchase
    def self.forsale(user)
      sql =<<SQL
          SELECT jt.* FROM jam_tracks jt 
          WHERE jt.id NOT IN (
                              SELECT jt.id 
                              FROM jam_tracks jt 
                              JOIN jam_track_rights AS jtr ON jtr.jam_track_id = jt.id 
                              WHERE jtr.user_id = '#{user.id}'
                              )
          LIMIT 1
SQL
      self.find_by_sql(sql).first
    end

    def genre_name
      self.genres.first.try(:description)
    end

  end
end
