# Copyright 2011-2025, The Trustees of Indiana University and Northwestern
#   University.  Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
#   under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
#   CONDITIONS OF ANY KIND, either express or implied. See the License for the
#   specific language governing permissions and limitations under the License.
# ---  END LICENSE_HEADER BLOCK  ---

module Avalon
  class FFprobe
    # @param [FileLocator] media_file a file locator instance for the media file
    def initialize(media_file)
      @media_file = media_file
    end

    def json_output
      return @json_output unless @json_output.nil?
      return @json_output = {} unless valid_content_type?(@media_file)
      ffprobe = Settings&.ffprobe&.path || 'ffprobe'
      # Include authorization headers for cases like Google Drive
      header = "-headers 'Authorization: #{@media_file.auth_header.fetch('Authorization')}'" if @media_file.auth_header.present?
      raw_output = `#{ffprobe} #{header} -i "#{@media_file.location}" -v quiet -show_format -show_streams -of json`
      
      if raw_output.empty?
        Rails.logger.error "File processing failed. Please ensure that FFprobe is installed and that the correct path is configured."
        return @json_output = {}
      end
      @json_output = JSON.parse(raw_output).deep_symbolize_keys
      @json_output
    end

    def video_stream
      return unless json_output.present?
      @video_stream ||= json_output[:streams].select { |stream| stream[:codec_type] == 'video' }.first
    end

    def audio_stream
      return unless json_output.present?
      @audio_stream ||= json_output[:streams].select { |stream| stream[:codec_type] == 'audio' }.first
    end

    def video?
      # Audio only mp4s still register as 'video/mp4' so check for presence of video stream
      # May still return false positives if mp4 has album art
      content_type(@media_file).start_with?('video') && video_stream.present?
    end

    def audio?
      # Prioritize mimetype check, fallback to audio stream for mp4
      (content_type(@media_file).start_with?('audio') && audio_stream.present?) || audio_stream.present?
    end

    def duration
      return unless video? || audio?
      # ffprobe return duration as seconds. Existing Avalon logic expects milliseconds.
      (json_output[:format][:duration].to_f * 1000).to_i
    end

    def display_aspect_ratio
      if video? && video_stream[:display_aspect_ratio].present?
        video_stream[:display_aspect_ratio]
      elsif video?
        (video_stream[:width].to_f / video_stream[:height].to_f).to_s
      end
    end

    def original_frame_size
      "#{video_stream[:width]}x#{video_stream[:height]}" if video?
    end

    private

    def content_type media_file
      # Remove S3 credentials or other params from extension output
      extension = File.extname(media_file.location)&.gsub(/[\?#].*/, '')
      # Fall back on file extension if magic bytes fail to identify file
      Marcel::MimeType.for media_file.reader, extension: extension
    end

    def valid_content_type? media_file
      ['audio', 'video'].any? { |type| content_type(media_file).include?(type) }
    end
  end
end
