# frozen_string_literal: true

class Imports::SecureFileDownloader
  DOWNLOAD_TIMEOUT = 300 # 5 minutes timeout
  MAX_RETRIES = 3

  def initialize(storage_attachment)
    @storage_attachment = storage_attachment
  end

  def download_with_verification
    file_content = download_to_string
    verify_file_integrity(file_content)
    file_content
  end

  def download_to_temp_file
    retries = 0
    temp_file = nil

    begin
      Timeout.timeout(DOWNLOAD_TIMEOUT) do
        temp_file = create_temp_file
        
        # Download directly to temp file
        storage_attachment.download do |chunk|
          temp_file.write(chunk)
        end
        temp_file.rewind

        # If file is empty, try alternative download method
        if temp_file.size == 0
          Rails.logger.warn('No content received from block download, trying alternative method')
          temp_file.write(storage_attachment.blob.download)
          temp_file.rewind
        end
      end
    rescue Timeout::Error => e
      retries += 1
      if retries <= MAX_RETRIES
        Rails.logger.warn("Download timeout, attempt #{retries} of #{MAX_RETRIES}")
        cleanup_temp_file(temp_file)
        retry
      else
        Rails.logger.error("Download failed after #{MAX_RETRIES} attempts")
        cleanup_temp_file(temp_file)
        raise
      end
    rescue StandardError => e
      Rails.logger.error("Download error: #{e.message}")
      cleanup_temp_file(temp_file)
      raise
    end

    raise 'Download completed but no content was received' if temp_file.size == 0

    verify_temp_file_integrity(temp_file)
    temp_file.path
  ensure
    # Keep temp file open so it can be read by other processes
    # Caller is responsible for cleanup
  end

  private

  attr_reader :storage_attachment

  def download_to_string
    retries = 0
    file_content = nil

    begin
      Timeout.timeout(DOWNLOAD_TIMEOUT) do
        # Download the file to a string
        tempfile = Tempfile.new("download_#{Time.now.to_i}", binmode: true)
        begin
          # Try to download block-by-block
          storage_attachment.download do |chunk|
            tempfile.write(chunk)
          end
          tempfile.rewind
          file_content = tempfile.read
        ensure
          tempfile.close
          tempfile.unlink
        end

        # If we didn't get any content but no error occurred, try a different approach
        if file_content.nil? || file_content.empty?
          Rails.logger.warn('No content received from block download, trying alternative method')
          # Some ActiveStorage attachments may work differently, try direct access if possible
          file_content = storage_attachment.blob.download
        end
      end
    rescue Timeout::Error => e
      retries += 1
      if retries <= MAX_RETRIES
        Rails.logger.warn("Download timeout, attempt #{retries} of #{MAX_RETRIES}")
        retry
      else
        Rails.logger.error("Download failed after #{MAX_RETRIES} attempts")
        raise
      end
    rescue StandardError => e
      Rails.logger.error("Download error: #{e.message}")
      raise
    end

    raise 'Download completed but no content was received' if file_content.nil? || file_content.empty?

    file_content
  end

  def create_temp_file
    extension = File.extname(storage_attachment.filename.to_s)
    basename = File.basename(storage_attachment.filename.to_s, extension)
    Tempfile.new(["#{basename}_#{Time.now.to_i}", extension], binmode: true)
  end

  def cleanup_temp_file(temp_file)
    return unless temp_file

    temp_file.close unless temp_file.closed?
    temp_file.unlink if File.exist?(temp_file.path)
  rescue StandardError => e
    Rails.logger.warn("Failed to cleanup temp file: #{e.message}")
  end

  def verify_file_integrity(file_content)
    return if file_content.nil? || file_content.empty?

    # Verify file size
    expected_size = storage_attachment.blob.byte_size
    actual_size = file_content.bytesize

    if expected_size != actual_size
      raise "Incomplete download: expected #{expected_size} bytes, got #{actual_size} bytes"
    end

    # Verify checksum
    expected_checksum = storage_attachment.blob.checksum
    actual_checksum = Base64.strict_encode64(Digest::MD5.digest(file_content))

    return unless expected_checksum != actual_checksum

    raise "Checksum mismatch: expected #{expected_checksum}, got #{actual_checksum}"
  end

  def verify_temp_file_integrity(temp_file)
    return if temp_file.nil? || temp_file.size == 0

    # Verify file size
    expected_size = storage_attachment.blob.byte_size
    actual_size = temp_file.size

    if expected_size != actual_size
      raise "Incomplete download: expected #{expected_size} bytes, got #{actual_size} bytes"
    end

    # Verify checksum
    expected_checksum = storage_attachment.blob.checksum
    temp_file.rewind
    actual_checksum = Base64.strict_encode64(Digest::MD5.digest(temp_file.read))
    temp_file.rewind

    return unless expected_checksum != actual_checksum

    raise "Checksum mismatch: expected #{expected_checksum}, got #{actual_checksum}"
  end
end
