# frozen_string_literal: true

class BulkImportService < BaseService
  def call(import)
    @import  = import
    @account = @import.account

    case @import.type.to_sym
    when :following
      import_follows!
    when :blocking
      import_blocks!
    when :muting
      import_mutes!
    when :domain_blocking
      import_domain_blocks!
    when :bookmarks
      import_bookmarks!
    when :lists
      import_lists!
    end

    @import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items
  rescue
    @import.update!(state: :finished, finished_at: Time.now.utc)

    raise
  end

  private

  def extract_rows_by_acct
    local_domain_suffix = "@#{Rails.configuration.x.local_domain}"
    @import.rows.to_a.index_by { |row| row.data['acct'].delete_suffix(local_domain_suffix) }
  end

  def import_follows!
    rows_by_acct = extract_rows_by_acct

    if @import.overwrite?
      @account.following.reorder(nil).find_each do |followee|
        row = rows_by_acct.delete(followee.acct)

        if row.nil?
          UnfollowService.new.call(@account, followee)
        else
          row.destroy
          @import.processed_items += 1
          @import.imported_items += 1

          # Since we're updating the settings of an existing relationship, we can safely call
          # FollowService directly
          FollowService.new.call(@account, followee, reblogs: row.data['show_reblogs'], notify: row.data['notify'], languages: row.data['languages'])
        end
      end

      # Save pending infos due to `overwrite?` handling
      @import.save!
    end

    Import::RowWorker.push_bulk(rows_by_acct.values) do |row|
      [row.id]
    end
  end

  def import_blocks!
    rows_by_acct = extract_rows_by_acct

    if @import.overwrite?
      @account.blocking.reorder(nil).find_each do |blocked_account|
        row = rows_by_acct.delete(blocked_account.acct)

        if row.nil?
          UnblockService.new.call(@account, blocked_account)
        else
          row.destroy
          @import.processed_items += 1
          @import.imported_items += 1
          BlockService.new.call(@account, blocked_account)
        end
      end

      # Save pending infos due to `overwrite?` handling
      @import.save!
    end

    Import::RowWorker.push_bulk(rows_by_acct.values) do |row|
      [row.id]
    end
  end

  def import_mutes!
    rows_by_acct = extract_rows_by_acct

    if @import.overwrite?
      @account.muting.reorder(nil).find_each do |muted_account|
        row = rows_by_acct.delete(muted_account.acct)

        if row.nil?
          UnmuteService.new.call(@account, muted_account)
        else
          row.destroy
          @import.processed_items += 1
          @import.imported_items += 1
          MuteService.new.call(@account, muted_account, notifications: row.data['hide_notifications'])
        end
      end

      # Save pending infos due to `overwrite?` handling
      @import.save!
    end

    Import::RowWorker.push_bulk(rows_by_acct.values) do |row|
      [row.id]
    end
  end

  def import_domain_blocks!
    domains = @import.rows.map { |row| row.data['domain'] }

    if @import.overwrite?
      @account.domain_blocks.find_each do |domain_block|
        domain = domains.delete(domain_block)

        @account.unblock_domain!(domain_block.domain) if domain.nil?
      end
    end

    @import.rows.delete_all
    domains.each { |domain| @account.block_domain!(domain) }
    @import.update!(processed_items: @import.total_items, imported_items: @import.total_items)

    AfterAccountDomainBlockWorker.push_bulk(domains) do |domain|
      [@account.id, domain]
    end
  end

  def import_bookmarks!
    rows_by_uri = @import.rows.index_by { |row| row.data['uri'] }

    if @import.overwrite?
      @account.bookmarks.includes(:status).find_each do |bookmark|
        row = rows_by_uri.delete(ActivityPub::TagManager.instance.uri_for(bookmark.status))

        if row.nil?
          bookmark.destroy!
        else
          row.destroy
          @import.processed_items += 1
          @import.imported_items += 1
        end
      end

      # Save pending infos due to `overwrite?` handling
      @import.save!
    end

    Import::RowWorker.push_bulk(rows_by_uri.values) do |row|
      [row.id]
    end
  end

  def import_lists!
    rows = @import.rows.to_a
    included_lists = rows.map { |row| row.data['list_name'] }.uniq

    if @import.overwrite?
      @account.owned_lists.where.not(title: included_lists).destroy_all

      # As list membership changes do not retroactively change timeline
      # contents, simplify things by just clearing everything
      @account.owned_lists.find_each do |list|
        list.list_accounts.destroy_all
      end
    end

    included_lists.each do |title|
      @account.owned_lists.find_or_create_by!(title: title)
    end

    Import::RowWorker.push_bulk(rows) do |row|
      [row.id]
    end
  end
end