# frozen_string_literal: true

class ProtocolsImporterV2
  include RenamingUtil

  def initialize(user, team)
    @user = user
    @team = team
  end

  def import_new_protocol(protocol_json, type)
    remove_empty_inputs(protocol_json)
    protocol = Protocol.new(
      name: protocol_json['name'],
      authors: protocol_json['authors'],
      protocol_type: (type == :public ? :in_repository_public : :in_repository_private),
      published_on: (type == :public ? Time.now : nil),
      added_by: @user,
      team: @team
    )

    # Try to rename record
    rename_record(protocol, :name) if protocol.invalid?

    # Okay, now save the protocol
    protocol.save!

    # Protocol is saved, populate it
    populate_protocol(protocol, protocol_json)

    protocol
  end

  def import_into_existing(protocol, protocol_json)
    # Firstly, destroy existing protocol's contents
    protocol.tiny_mce_assets.destroy_all
    protocol.destroy_contents
    protocol.reload

    # Alright, now populate the protocol
    populate_protocol(protocol, protocol_json)
    protocol.reload

    # Unlink the protocol
    protocol.unlink
    protocol
  end

  private

  def create_in_step!(step, new_orderable)
    ActiveRecord::Base.transaction do
      new_orderable.save!
      step.step_orderable_elements.create!(
        position: step.step_orderable_elements.length,
        orderable: new_orderable
      )
    end
  end

  def populate_protocol(protocol, protocol_json)
    protocol.reload
    protocol.description = populate_rte(protocol_json, protocol)
    protocol.name = protocol_json['name'].presence
    protocol.save!
    asset_ids = []
    step_pos = 0

    # Check if protocol has steps
    protocol_json['steps']&.values&.each do |step_json|
      step = Step.create!(
        name: step_json['name'],
        position: step_pos,
        completed: false,
        user: @user,
        last_modified_by: @user,
        protocol: protocol
      )

      step.save!
      step_pos += 1

      step_json['stepElements']&.values&.each do |element_params|
        case element_params['type']
        when 'StepText'
          create_step_text(step, element_params['stepText'])
        when 'StepTable'
          create_step_table(step, element_params['elnTable'])
        when 'Checklist'
          create_checklist(step, element_params['checklist'])
        end
      end

      next unless step_json['assets']

      asset_ids += create_assets(step_json, step)
    end

    # Post process assets
    asset_ids.each do |asset_id|
      Asset.find(asset_id).post_process_file(protocol.team)
    end
  end

  def create_assets(step_json, step)
    asset_ids = []
    step_json['assets']&.values&.each do |asset_json|
      asset = Asset.new(
        created_by: @user,
        last_modified_by: @user,
        team: @team
      )

      # Decode the file bytes
      asset.file.attach(io: StringIO.new(Base64.decode64(asset_json['bytes'])),
                        filename: asset_json['fileName'],
                        content_type: asset_json['fileType'],
                        metadata: JSON.parse(asset_json['fileMetadata'] || '{}'))
      asset.save!
      asset_ids << asset.id

      StepAsset.create!(
        step: step,
        asset: asset
      )
    end

    asset_ids
  end

  def create_step_text(step, params)
    step_text = StepText.create!(
      step: step
    )

    step_text.update!(text: populate_rte(params, step_text))

    create_in_step!(step, step_text)
  end

  def create_step_table(step, params)
    step_table = StepTable.new(
      step: step,
      table: Table.new(
        name: params['name'],
        contents: Base64.decode64(params['contents']),
        created_by: @user,
        last_modified_by: @user,
        team: @team
      )
    )

    create_in_step!(step, step_table)
  end

  def create_checklist(step, params)
    checklist = Checklist.new(
      name: params['name'],
      step: step,
      created_by: @user,
      last_modified_by: @user
    )

    create_in_step!(step, checklist)

    return unless params['items']

    item_pos = 0

    params['items'].each_value do |item_json|
      ChecklistItem.create!(
        text: item_json['text'],
        checked: false,
        position: item_pos,
        created_by: @user,
        last_modified_by: @user,
        checklist: checklist
      )
      item_pos += 1
    end
  end

  def remove_empty_inputs(obj)
    obj.each_key do |key|
      case obj[key]
      when ''
        obj[key] = nil
      when Hash
        # Recursive call
        remove_empty_inputs(obj[key])
      end
    end
  end

  # create tiny_mce assets and change the import tokens
  def populate_rte(params, object)
    description = params['description'] || params['contents']

    return description unless params['descriptionAssets']

    params['descriptionAssets'].each_value do |tiny_mce_img_json|
      tiny_mce_img = TinyMceAsset.new(
        object: object,
        team_id: @team.id,
        saved: true
      )
      tiny_mce_img.save!

      # Decode the file bytes
      file = StringIO.new(Base64.decode64(tiny_mce_img_json['bytes']))
      to_blob = ActiveStorage::Blob.create_and_upload!(
        io: file,
        filename: tiny_mce_img_json['fileName'],
        content_type: tiny_mce_img_json['fileType'],
        metadata: JSON.parse(tiny_mce_img_json['fileMetadata'] || '{}')
      )
      tiny_mce_img.image.attach(to_blob)
      description.gsub!(
        "data-mce-token=\"#{tiny_mce_img_json['tokenId']}\"",
        "data-mce-token=\"#{Base62.encode(tiny_mce_img.id)}\""
      ) || description.gsub!(
        "data-mce-token=\"#{Base62.encode(tiny_mce_img_json['tokenId'].to_i)}\"",
        "data-mce-token=\"#{Base62.encode(tiny_mce_img.id)}\""
      )
      description.gsub!('  ]]--&gt;', '')
    end
    description
  end
end