<commit_before># frozen_string_literal: true

class TinyMceAsset < ApplicationRecord
  extend ProtocolsExporter
  extend MarvinJsActions
  include ActiveStorageConcerns

  attr_accessor :reference
  before_create :set_reference
  after_create :calculate_estimated_size, :self_destruct
  after_destroy :release_team_space

  belongs_to :team, inverse_of: :tiny_mce_assets, optional: true

  belongs_to :object, polymorphic: true,
                      optional: true,
                      inverse_of: :tiny_mce_assets

  has_one_attached :image

  validates :estimated_size, presence: true

  def self.update_images(object, images, current_user)
    images = JSON.parse(images)
    current_images = object.tiny_mce_assets.pluck(:id)
    images_to_delete = current_images.reject do |x|
      (images.include? Base62.encode(x))
    end
    images.each do |image|
      image_to_update = find_by(id: Base62.decode(image))
      next if image_to_update.object || image_to_update.team_id != Team.search_by_object(object).id

      image_to_update&.update(object: object, saved: true)
      create_create_marvinjs_activity(image_to_update, current_user)
    end

    where(id: images_to_delete).find_each do |image_to_delete|
      create_delete_marvinjs_activity(image_to_delete, current_user)
      image_to_delete.destroy
    end

    object.delay(queue: :assets).copy_unknown_tiny_mce_images
  rescue StandardError => e
    Rails.logger.error e.message
  end

  def self.generate_url(description, obj = nil)
    # Check tinymce for old format
    description = update_old_tinymce(description, obj)

    description = Nokogiri::HTML(description)
    tm_assets = description.css('img[data-mce-token]')
    tm_assets.each do |tm_asset|
      asset_id = tm_asset.attr('data-mce-token')
      new_asset = obj.tiny_mce_assets.find_by(id: Base62.decode(asset_id))
      if new_asset&.image&.attached?
        tm_asset.attributes['src'].value = Rails.application.routes.url_helpers.url_for(new_asset.image)
        tm_asset['class'] = 'img-responsive'
      end
    end
    description.css('body').inner_html.to_s
  end

  def file_name
    return '' unless image.attached?

    image.blob&.filename&.sanitized
  end

  def file_size
    return 0 unless image.attached?

    image.blob&.byte_size
  end

  def content_type
    return '' unless image.attached?

    image&.blob&.content_type
  end

  def preview
    image.variant(resize_to_limit: Constants::LARGE_PIC_FORMAT)
  end

  def self.delete_unsaved_image(id)
    asset = find_by(id: id)
    asset.destroy if asset && !asset.saved
  end

  def self.update_estimated_size(id)
    asset = find_by(id: id)
    return unless asset&.image&.attached?

    size = asset.image.blob.byte_size
    return if size.blank?

    e_size = size * Constants::ASSET_ESTIMATED_SIZE_FACTOR
    asset.update(estimated_size: e_size)
    Rails.logger.info "Asset #{id}: Estimated size successfully calculated"
    # update team space taken
    asset.team.take_space(e_size)
    asset.team.save
  end

  def self.update_old_tinymce(description, obj = nil)
    return description unless description

    description.scan(/\[~tiny_mce_id:(\w+)\]/).flatten.each do |token|
      old_format = /\[~tiny_mce_id:#{token}\]/
      new_format = "<img src=\"\" class=\"img-responsive\" data-mce-token=\"#{Base62.encode(token.to_i)}\"/>"

      asset = find_by(id: token)
      # impor flag only for import from file cases, because we don't have image in DB
      unless asset || import
        # remove tag if asset deleted
        description.sub!(old_format, '')
        next
      end

      # If object (step or result text) don't have direct assciation to tinyMCE image, we need copy it.
      asset.clone_tinymce_asset(obj) if obj && obj != asset.object

      description.sub!(old_format, new_format)
    end
    description
  end

  def self.save_to_eln(ostream, dir)
    if exists?
      order(:id).each do |tiny_mce_asset|
        asset_guid = get_guid(tiny_mce_asset.id)
        asset_file_name = "rte-#{asset_guid}.#{tiny_mce_asset.image.blob.filename.extension}"
        ostream.put_next_entry("#{dir}/#{asset_file_name}")
        ostream.print(tiny_mce_asset.image.download)
      end
    end
    ostream
  end

  def clone_tinymce_asset(obj)
    begin
      # It will trigger only for Step or ResultText
      team_id = if obj.class.name == 'Step'
                  obj.protocol.team_id
                else
                  obj.result.my_module.protocol.team_id
                end
    rescue StandardError => e
      Rails.logger.error e.message
      team_id = nil
    end

    return false unless team_id
<commit_msg>Fix eln export for file without extension<commit_after># frozen_string_literal: true

class TinyMceAsset < ApplicationRecord
  extend ProtocolsExporter
  extend MarvinJsActions
  include ActiveStorageConcerns

  attr_accessor :reference
  before_create :set_reference
  after_create :calculate_estimated_size, :self_destruct
  after_destroy :release_team_space

  belongs_to :team, inverse_of: :tiny_mce_assets, optional: true

  belongs_to :object, polymorphic: true,
                      optional: true,
                      inverse_of: :tiny_mce_assets

  has_one_attached :image

  validates :estimated_size, presence: true

  def self.update_images(object, images, current_user)
    images = JSON.parse(images)
    current_images = object.tiny_mce_assets.pluck(:id)
    images_to_delete = current_images.reject do |x|
      (images.include? Base62.encode(x))
    end
    images.each do |image|
      image_to_update = find_by(id: Base62.decode(image))
      next if image_to_update.object || image_to_update.team_id != Team.search_by_object(object).id

      image_to_update&.update(object: object, saved: true)
      create_create_marvinjs_activity(image_to_update, current_user)
    end

    where(id: images_to_delete).find_each do |image_to_delete|
      create_delete_marvinjs_activity(image_to_delete, current_user)
      image_to_delete.destroy
    end

    object.delay(queue: :assets).copy_unknown_tiny_mce_images
  rescue StandardError => e
    Rails.logger.error e.message
  end

  def self.generate_url(description, obj = nil)
    # Check tinymce for old format
    description = update_old_tinymce(description, obj)

    description = Nokogiri::HTML(description)
    tm_assets = description.css('img[data-mce-token]')
    tm_assets.each do |tm_asset|
      asset_id = tm_asset.attr('data-mce-token')
      new_asset = obj.tiny_mce_assets.find_by(id: Base62.decode(asset_id))
      if new_asset&.image&.attached?
        tm_asset.attributes['src'].value = Rails.application.routes.url_helpers.url_for(new_asset.image)
        tm_asset['class'] = 'img-responsive'
      end
    end
    description.css('body').inner_html.to_s
  end

  def file_name
    return '' unless image.attached?

    image.blob&.filename&.sanitized
  end

  def file_size
    return 0 unless image.attached?

    image.blob&.byte_size
  end

  def content_type
    return '' unless image.attached?

    image&.blob&.content_type
  end

  def preview
    image.variant(resize_to_limit: Constants::LARGE_PIC_FORMAT)
  end

  def self.delete_unsaved_image(id)
    asset = find_by(id: id)
    asset.destroy if asset && !asset.saved
  end

  def self.update_estimated_size(id)
    asset = find_by(id: id)
    return unless asset&.image&.attached?

    size = asset.image.blob.byte_size
    return if size.blank?

    e_size = size * Constants::ASSET_ESTIMATED_SIZE_FACTOR
    asset.update(estimated_size: e_size)
    Rails.logger.info "Asset #{id}: Estimated size successfully calculated"
    # update team space taken
    asset.team.take_space(e_size)
    asset.team.save
  end

  def self.update_old_tinymce(description, obj = nil)
    return description unless description

    description.scan(/\[~tiny_mce_id:(\w+)\]/).flatten.each do |token|
      old_format = /\[~tiny_mce_id:#{token}\]/
      new_format = "<img src=\"\" class=\"img-responsive\" data-mce-token=\"#{Base62.encode(token.to_i)}\"/>"

      asset = find_by(id: token)
      # impor flag only for import from file cases, because we don't have image in DB
      unless asset || import
        # remove tag if asset deleted
        description.sub!(old_format, '')
        next
      end

      # If object (step or result text) don't have direct assciation to tinyMCE image, we need copy it.
      asset.clone_tinymce_asset(obj) if obj && obj != asset.object

      description.sub!(old_format, new_format)
    end
    description
  end

  def self.save_to_eln(ostream, dir)
    if exists?
      order(:id).each do |tiny_mce_asset|
        asset_guid = get_guid(tiny_mce_asset.id)
        extension = tiny_mce_asset.image.blob.filename.extension
        asset_file_name = if extension.empty?
                            "rte-#{asset_guid}"
                          else
                            "rte-#{asset_guid}.#{tiny_mce_asset.image.blob.filename.extension}"
                          end
        ostream.put_next_entry("#{dir}/#{asset_file_name}")
        ostream.print(tiny_mce_asset.image.download)
      end
    end
    ostream
  end

  def clone_tinymce_asset(obj)
    begin
      # It will trigger only for Step or ResultText
      team_id = if obj.class.name == 'Step'
                  obj.protocol.team_id
                else
                  obj.result.my_module.protocol.team_id
                end
    rescue StandardError => e
      Rails.logger.error e.message
      team_id = nil
    end

    return false unless team_id

{'fraction_non_alphanumeric': 0.09325936780406113, 'fraction_numerical': 0.0018840276324052752, 'mean_word_length': 3.0435886584849765, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '941370', 'n_tokens_mistral': 3146, 'n_tokens_neox': 3050, 'n_words': 645}