# frozen_string_literal: true

require File.expand_path("../../config/environment", __FILE__)

# no less than 1 megapixel
max_image_pixels = [ARGV[0].to_i, 1_000_000].max

puts "", "Downsizing images to no more than #{max_image_pixels} pixels"

dimensions_count = 0
downsized_count = 0

def downsize_upload(upload, path, max_image_pixels)
  # Make sure the filesize is up to date
  upload.filesize = File.size(path)

  OptimizedImage.downsize(path, path, "#{max_image_pixels}@", filename: upload.original_filename)
  sha1 = Upload.generate_digest(path)

  if sha1 == upload.sha1
    puts "no sha1 change" if ENV["VERBOSE"]
    return
  end

  w, h = FastImage.size(path, timeout: 10, raise_on_failure: true)

  if !w || !h
    puts "invalid image dimensions after resizing" if ENV["VERBOSE"]
    return
  end

  # Neither #dup or #clone provide a complete copy
  original_upload = Upload.find(upload.id)
  ww, hh = ImageSizer.resize(w, h)
  new_file = true

  if existing_upload = Upload.find_by(sha1: sha1)
    upload = existing_upload
    new_file = false
  end

  before = upload.filesize
  upload.filesize = File.size(path)

  if upload.filesize > before
    puts "no filesize reduction" if ENV["VERBOSE"]
    return
  end

  upload.sha1 = sha1
  upload.width = w
  upload.height = h
  upload.thumbnail_width = ww
  upload.thumbnail_height = hh

  if new_file
    url = Discourse.store.store_upload(File.new(path), upload)

    unless url
      puts "couldn't store the upload" if ENV["VERBOSE"]
      return
    end

    upload.url = url
  end

  if ENV["VERBOSE"]
    puts "base62: #{original_upload.base62_sha1} -> #{Upload.base62_sha1(sha1)}"
    puts "sha1: #{original_upload.sha1} -> #{sha1}"
    puts "is a new file: #{new_file}"
  end

  upload.save!

  if new_file
    upload.optimized_images.each(&:destroy!)
    Discourse.store.remove_upload(original_upload)
  else
    begin
      PostUpload.where(upload_id: original_upload.id).update_all(upload_id: upload.id)
    rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation
    end

    User.where(uploaded_avatar_id: original_upload.id).update_all(uploaded_avatar_id: upload.id)
    UserAvatar.where(gravatar_upload_id: original_upload.id).update_all(gravatar_upload_id: upload.id)
    UserAvatar.where(custom_upload_id: original_upload.id).update_all(custom_upload_id: upload.id)
    UserProfile.where(profile_background_upload_id: original_upload.id).update_all(profile_background_upload_id: upload.id)
    UserProfile.where(card_background_upload_id: original_upload.id).update_all(card_background_upload_id: upload.id)
    Category.where(uploaded_logo_id: original_upload.id).update_all(uploaded_logo_id: upload.id)
    Category.where(uploaded_background_id: original_upload.id).update_all(uploaded_background_id: upload.id)
    CustomEmoji.where(upload_id: original_upload.id).update_all(upload_id: upload.id)
    ThemeField.where(upload_id: original_upload.id).update_all(upload_id: upload.id)
  end

  original_upload.posts.each do |post|
    post.raw.gsub!(/upload:\/\/#{original_upload.base62_sha1}(\.#{original_upload.extension})?/, upload.short_url)
    post.raw.gsub!(Discourse.store.cdn_url(original_upload.url), Discourse.store.cdn_url(upload.url))

    if post.raw_changed?
      puts "updating post #{post.id}" if ENV["VERBOSE"]
      post.save!
    else
      puts "Could find the upload path in post.raw (post_id: #{post.id})" if ENV["VERBOSE"]
    end

    post.rebake!
  end

  original_upload.reload.destroy! unless new_file

  puts "" if ENV["VERBOSE"]

  true
end

scope = Upload
  .where("LOWER(extension) IN ('jpg', 'jpeg', 'gif', 'png')")
  .where("COALESCE(width, 0) = 0 OR COALESCE(height, 0) = 0 OR COALESCE(thumbnail_width, 0) = 0 OR COALESCE(thumbnail_height, 0) = 0 OR width * height > ?", max_image_pixels)

puts "Uploads to process: #{scope.count}"

scope.find_each do |upload|
  print "\rFixed dimensions: %8d        Downsized: %8d (upload id: #{upload.id})".freeze % [dimensions_count, downsized_count]
  puts "\n" if ENV["VERBOSE"]

  source = upload.local? ? Discourse.store.path_for(upload) : "https:#{upload.url}"

  unless source
    puts "no path or URL" if ENV["VERBOSE"]
    next
  end

  w, h = FastImage.size(source, timeout: 10)

  if !w || !h
    puts "invalid image dimensions" if ENV["VERBOSE"]
    next
  end

  ww, hh = ImageSizer.resize(w, h)

  if w == 0 || h == 0 || ww == 0 || hh == 0
    puts "invalid image dimensions" if ENV["VERBOSE"]
    next
  end

  if upload.read_attribute(:width) != w || upload.read_attribute(:height) != h || upload.read_attribute(:thumbnail_width) != ww || upload.read_attribute(:thumbnail_height) != hh
    puts "Correcting the upload dimensions" if ENV["VERBOSE"]
    dimensions_count += 1

    upload.update!(
      width: w,
      height: h,
      thumbnail_width: ww,
      thumbnail_height: hh,
    )
  end

  if w * h < max_image_pixels
    puts "image size within allowed range" if ENV["VERBOSE"]
    next
  end

  path = upload.local? ? source : (Discourse.store.download(upload) rescue nil)&.path

  unless path
    puts "no image path" if ENV["VERBOSE"]
    next
  end

  downsized_count += 1 if downsize_upload(upload, path, max_image_pixels)
end

puts "", "Done"