# frozen_string_literal: true

class ReplaceExternalWikiTriggers < ActiveRecord::Migration[6.1]
  include Gitlab::Database::SchemaHelpers

  def up
    replace_triggers('type_new', 'Integrations::ExternalWiki')

    # we need an extra trigger to handle when type_new is updated by the
    # `integrations_set_type_new` trigger.
    # This can be removed when this trigger has been removed.
    execute(<<~SQL.squish)
      CREATE TRIGGER #{trigger_name(:type_new_updated)}
        AFTER UPDATE OF type_new ON integrations FOR EACH ROW
        WHEN ((new.type_new)::text = 'Integrations::ExternalWiki'::text AND new.project_id IS NOT NULL)
        EXECUTE FUNCTION set_has_external_wiki();
    SQL
  end

  def down
    execute("DROP TRIGGER IF EXISTS #{trigger_name(:type_new_updated)} ON integrations;")
    replace_triggers('type', 'ExternalWikiService')
  end

  private

  def replace_triggers(column_name, value)
    triggers(column_name, value).each do |event, condition|
      trigger = trigger_name(event)

      # create duplicate trigger, using the defined condition
      execute(<<~SQL.squish)
      CREATE TRIGGER #{trigger}_new AFTER #{event.upcase} ON integrations FOR EACH ROW
        WHEN (#{condition})
        EXECUTE FUNCTION set_has_external_wiki();
      SQL

      # Swap the triggers in place, so that the new trigger has the canonical name
      execute("ALTER TRIGGER #{trigger} ON integrations RENAME TO #{trigger}_old;")
      execute("ALTER TRIGGER #{trigger}_new ON integrations RENAME TO #{trigger};")

      # remove the old, now redundant trigger
      execute("DROP TRIGGER IF EXISTS #{trigger}_old ON integrations;")
    end
  end

  def trigger_name(event)
    "trigger_has_external_wiki_on_#{event}"
  end

  def triggers(column_name, value)
    {
      delete: "#{matches_value('old', column_name, value)} AND #{project_not_null('old')}",
      insert: "(new.active = true) AND #{matches_value('new', column_name, value)} AND #{project_not_null('new')}",
      update: "#{matches_value('new', column_name, value)} AND (old.active <> new.active) AND #{project_not_null('new')}"
    }
  end

  def project_not_null(row)
    "(#{row}.project_id IS NOT NULL)"
  end

  def matches_value(row, column_name, value)
    "((#{row}.#{column_name})::text = '#{value}'::text)"
  end
end