class MatchesPresenter
  def initialize(video, video_analysis, audio_analysis, edl_events, graphics_edl_events)
    @video = video
    @video_analysis = video_analysis
    @audio_analysis = audio_analysis
    @edl_events = edl_events
    @graphics_edl_events = graphics_edl_events
  end

  def build_chronological_matches
    if video.analysis_success?
      sort_matches(first_appearances_with_images)
    else
      []
    end
  end

  def build_chronological_audio_matches
    if video.audio_analysis_success?
      sort_matches(audio_matches)
    else
      []
    end
  end

  def build_graphics_matches
    if video.analysis_success?
      confirmed_graphics = video.graphics_elements

      sort_matches(
        graphics_edl_events.
          reject { |edl_event| edl_event.source_file_name.blank? }.
          map do |edl_event|
            GraphicsMatch.new(
              edl_event.source_file_name,
              edl_event.start_time,
              already_confirmed?(confirmed_graphics, edl_event),
              edl_event.timecode_in
            )
          end.
          compact
      )
    else
      []
    end
  end

  def all_tracks_edl_events
    edl_events
  end

  private

  def audio_events
    if ENV["DISABLE_EDL_CHANNEL_FILTER"]
      edl_events
    else
      edl_events.find_all { |event| event.channel.include? "A" }
    end
  end

  def video_events
    if ENV["DISABLE_EDL_CHANNEL_FILTER"]
      edl_events
    else
      edl_events.find_all { |event| event.channel == "V" }
    end
  end

  def already_confirmed?(confirmation, edl_event)
    confirmation.any? { |confirmed| confirmed.source_file_name == edl_event.source_file_name && confirmed.timecode_in == edl_event.timecode_in }
  end

  attr_reader :video, :video_analysis, :audio_analysis, :edl_events, :graphics_edl_events

  def sort_matches(matches)
    matches.
      sort_by(&:start_time).
      reverse
  end

  def first_appearances_with_images
    video_analysis.first_appearances.
      reject { |appearance| appearance.external_image_id.blank? }.
      map { |appearance| SuggestedMatch.build_from_first_appearance(appearance, appearance.start_time) }
  end

  def audio_matches
    audio_analysis.results.map do |result|
      # If it's a library match, use the provided composer and publisher info
      # If it's an original music match, use the composer and publisher info we have
      # If it's an acquired media match, don't provide any composer and publisher info
      if result.type == "acquired_media"
        composers = ""
        publishers = ""
      elsif result.type == "original_music"
        file_info = FileInfo.find(result.uid)
        composers = file_info.releasable.composers.map { |composer| "#{composer.name}, #{composer.affiliation}, #{composer.percentage}%" }.join("|")
        publishers = file_info.releasable.publishers.map { |publisher| "#{publisher.name}, #{publisher.affiliation}, #{publisher.percentage}%" }.join("|")
      else
        composers = result.audio_data.composers.map { |composer| "#{composer.name}, #{composer.affiliation}, #{composer.percentage}" }.join("|")
        publishers = result.audio_data.publishers.map { |publisher| "#{publisher.name}, #{publisher.affiliation}, #{publisher.percentage}" }.join("|")
      end

      result.edl.map do |edl_event|

        is_confirmed = video.audio_confirmations.any? do |confirmation| 
          # TODO: Use EDL ID in the future to determine this
          confirmation.source_file_name == result.requested_filename && 
            confirmation.timecode_in == edl_event.timecode_in 
        end

        result_type = result.type == "library" ? "library_music" : result.type 

        AudioSuggestedMatch.new(
          AudioData.new(
            result.audio_data.filename,
            result.requested_filename,
            composers,
            publishers,
            result.audio_data.catalog,
            result.audio_data.title,
          ),
          edl_event.start_time.to_i,
          is_confirmed,
          edl_event.timecode_in,
          result_type,
          video.audio_only_edl_file.attached?
        )
      end

    end.flatten
  end

  class GraphicsMatch
    attr_reader :appears_at_timecode, :start_time, :filename, :confirmed, :timecode_in

    def initialize(filename, start_time, confirmed, timecode_in)
      @filename = filename
      @start_time = start_time
      @appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
      @confirmed = confirmed
      @timecode_in = timecode_in
    end

    def elapsed_time_until_appearance
      appears_at_timecode.to_seconds
    end
  end

  class AudioSuggestedMatch
    attr_reader :appears_at_timecode, :start_time, :confirmed, :timecode_in, :confirmation_type, :edl_type

    def initialize(audio_data, start_time, confirmed, timecode_in, confirmation_type, audio_edl_attached)
      @audio_data = audio_data
      @start_time = start_time
      @confirmed = confirmed
      @appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
      @timecode_in = timecode_in
      @confirmation_type = confirmation_type
      @edl_type = audio_edl_attached ? "audio" : "all_tracks"
    end

    def presented_filename
      if confirmation_type == "original_music"
        "(O) #{audio_data.requested_filename}"
      else
        "(L) #{audio_data.requested_filename}"
      end
    end

    def filename
      audio_data.requested_filename
    end

    def composer_info
      audio_data.composer
    end

    def publisher_info
      audio_data.publisher
    end

    def catalog
      audio_data.catalog
    end

    def title
      audio_data.title
    end

    def elapsed_time_until_appearance
      appears_at_timecode.to_seconds
    end

    private

    attr_reader :audio_data
  end

  class FileInfoSuggestedMatch < SimpleDelegator
    attr_reader :appears_at_timecode, :start_time, :file_info, :timecode_in

    def self.build(file_info, start_time, timecode_in)
      release = file_info.releasable

      new(release, file_info, start_time, timecode_in)
    end

    def initialize(release, file_info, start_time, timecode_in)
      super(release)
      @file_info = file_info
      @start_time = start_time
      @appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
      @timecode_in = timecode_in
    end

    def elapsed_time_until_appearance
      appears_at_timecode.to_seconds
    end
  end

  class SuggestedMatch < SimpleDelegator
    attr_reader :appears_at_timecode, :start_time

    def self.build_from_first_appearance(appearance, start_time)
      release = ExternalImage.new(appearance.external_image_id).release

      new(release, start_time)
    end

    def initialize(release, start_time)
      super(release)
      @start_time = start_time
      @appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
    end

    def elapsed_time_until_appearance
      appears_at_timecode.to_seconds
    end
  end

  class ExternalImage
    attr_reader :uid

    def initialize(uid)
      @uid = uid
    end

    def release
      release_klass.find(id)
    end

    private

    def id
      uid.match(/.*_release_(?<id>\d*)/)
      Regexp.last_match(:id) || uid
    end

    def release_klass
      release_name.classify.constantize
    end

    def release_name
      uid.match(/(?<type>\w*_release)_\d/)
      Regexp.last_match(:type) || "appearance_release"
    end
  end
end
